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.model.building;
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.ArrayList;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.HashMap;
31  import java.util.Iterator;
32  import java.util.LinkedHashSet;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Objects;
36  import java.util.Optional;
37  import java.util.Properties;
38  import java.util.function.Consumer;
39  import java.util.stream.IntStream;
40  
41  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
42  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
43  import org.apache.maven.artifact.versioning.VersionRange;
44  import org.apache.maven.model.Activation;
45  import org.apache.maven.model.Build;
46  import org.apache.maven.model.Dependency;
47  import org.apache.maven.model.DependencyManagement;
48  import org.apache.maven.model.InputLocation;
49  import org.apache.maven.model.InputLocationTracker;
50  import org.apache.maven.model.InputSource;
51  import org.apache.maven.model.Model;
52  import org.apache.maven.model.Parent;
53  import org.apache.maven.model.Plugin;
54  import org.apache.maven.model.PluginManagement;
55  import org.apache.maven.model.Profile;
56  import org.apache.maven.model.Repository;
57  import org.apache.maven.model.building.ModelProblem.Severity;
58  import org.apache.maven.model.building.ModelProblem.Version;
59  import org.apache.maven.model.composition.DependencyManagementImporter;
60  import org.apache.maven.model.inheritance.InheritanceAssembler;
61  import org.apache.maven.model.interpolation.ModelInterpolator;
62  import org.apache.maven.model.io.ModelParseException;
63  import org.apache.maven.model.management.DependencyManagementInjector;
64  import org.apache.maven.model.management.PluginManagementInjector;
65  import org.apache.maven.model.normalization.ModelNormalizer;
66  import org.apache.maven.model.path.ModelPathTranslator;
67  import org.apache.maven.model.path.ModelUrlNormalizer;
68  import org.apache.maven.model.path.ProfileActivationFilePathInterpolator;
69  import org.apache.maven.model.plugin.LifecycleBindingsInjector;
70  import org.apache.maven.model.plugin.PluginConfigurationExpander;
71  import org.apache.maven.model.plugin.ReportConfigurationExpander;
72  import org.apache.maven.model.plugin.ReportingConverter;
73  import org.apache.maven.model.profile.DefaultProfileActivationContext;
74  import org.apache.maven.model.profile.ProfileActivationContext;
75  import org.apache.maven.model.profile.ProfileInjector;
76  import org.apache.maven.model.profile.ProfileSelector;
77  import org.apache.maven.model.resolution.InvalidRepositoryException;
78  import org.apache.maven.model.resolution.ModelResolver;
79  import org.apache.maven.model.resolution.UnresolvableModelException;
80  import org.apache.maven.model.resolution.WorkspaceModelResolver;
81  import org.apache.maven.model.superpom.SuperPomProvider;
82  import org.apache.maven.model.validation.ModelValidator;
83  import org.codehaus.plexus.interpolation.InterpolationException;
84  import org.codehaus.plexus.interpolation.MapBasedValueSource;
85  import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
86  import org.codehaus.plexus.interpolation.StringSearchInterpolator;
87  import org.codehaus.plexus.util.StringUtils;
88  import org.eclipse.sisu.Nullable;
89  
90  import static org.apache.maven.model.building.Result.error;
91  import static org.apache.maven.model.building.Result.newResult;
92  
93  /**
94   * @deprecated use {@code org.apache.maven.api.services.ModelBuilder} instead
95   */
96  @SuppressWarnings("UnusedReturnValue")
97  @Named
98  @Singleton
99  @Deprecated(since = "4.0.0")
100 public class DefaultModelBuilder implements ModelBuilder {
101     @Inject
102     private ModelProcessor modelProcessor;
103 
104     @Inject
105     private ModelValidator modelValidator;
106 
107     @Inject
108     private ModelNormalizer modelNormalizer;
109 
110     @Inject
111     private ModelInterpolator modelInterpolator;
112 
113     @Inject
114     private ModelPathTranslator modelPathTranslator;
115 
116     @Inject
117     private ModelUrlNormalizer modelUrlNormalizer;
118 
119     @Inject
120     private SuperPomProvider superPomProvider;
121 
122     @Inject
123     private InheritanceAssembler inheritanceAssembler;
124 
125     @Inject
126     private ProfileSelector profileSelector;
127 
128     @Inject
129     private ProfileInjector profileInjector;
130 
131     @Inject
132     private PluginManagementInjector pluginManagementInjector;
133 
134     @Inject
135     private DependencyManagementInjector dependencyManagementInjector;
136 
137     @Inject
138     private DependencyManagementImporter dependencyManagementImporter;
139 
140     @Inject
141     @Nullable
142     private LifecycleBindingsInjector lifecycleBindingsInjector;
143 
144     @Inject
145     private PluginConfigurationExpander pluginConfigurationExpander;
146 
147     @Inject
148     private ReportConfigurationExpander reportConfigurationExpander;
149 
150     @Inject
151     private ReportingConverter reportingConverter;
152 
153     @Inject
154     private ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator;
155 
156     public DefaultModelBuilder setModelProcessor(ModelProcessor modelProcessor) {
157         this.modelProcessor = modelProcessor;
158         return this;
159     }
160 
161     public DefaultModelBuilder setModelValidator(ModelValidator modelValidator) {
162         this.modelValidator = modelValidator;
163         return this;
164     }
165 
166     public DefaultModelBuilder setModelNormalizer(ModelNormalizer modelNormalizer) {
167         this.modelNormalizer = modelNormalizer;
168         return this;
169     }
170 
171     public DefaultModelBuilder setModelInterpolator(ModelInterpolator modelInterpolator) {
172         this.modelInterpolator = modelInterpolator;
173         return this;
174     }
175 
176     public DefaultModelBuilder setModelPathTranslator(ModelPathTranslator modelPathTranslator) {
177         this.modelPathTranslator = modelPathTranslator;
178         return this;
179     }
180 
181     public DefaultModelBuilder setModelUrlNormalizer(ModelUrlNormalizer modelUrlNormalizer) {
182         this.modelUrlNormalizer = modelUrlNormalizer;
183         return this;
184     }
185 
186     public DefaultModelBuilder setSuperPomProvider(SuperPomProvider superPomProvider) {
187         this.superPomProvider = superPomProvider;
188         return this;
189     }
190 
191     public DefaultModelBuilder setProfileSelector(ProfileSelector profileSelector) {
192         this.profileSelector = profileSelector;
193         return this;
194     }
195 
196     public DefaultModelBuilder setProfileInjector(ProfileInjector profileInjector) {
197         this.profileInjector = profileInjector;
198         return this;
199     }
200 
201     public DefaultModelBuilder setInheritanceAssembler(InheritanceAssembler inheritanceAssembler) {
202         this.inheritanceAssembler = inheritanceAssembler;
203         return this;
204     }
205 
206     public DefaultModelBuilder setDependencyManagementImporter(DependencyManagementImporter depMgmtImporter) {
207         this.dependencyManagementImporter = depMgmtImporter;
208         return this;
209     }
210 
211     public DefaultModelBuilder setDependencyManagementInjector(DependencyManagementInjector depMgmtInjector) {
212         this.dependencyManagementInjector = depMgmtInjector;
213         return this;
214     }
215 
216     public DefaultModelBuilder setLifecycleBindingsInjector(LifecycleBindingsInjector lifecycleBindingsInjector) {
217         this.lifecycleBindingsInjector = lifecycleBindingsInjector;
218         return this;
219     }
220 
221     public DefaultModelBuilder setPluginConfigurationExpander(PluginConfigurationExpander pluginConfigurationExpander) {
222         this.pluginConfigurationExpander = pluginConfigurationExpander;
223         return this;
224     }
225 
226     public DefaultModelBuilder setPluginManagementInjector(PluginManagementInjector pluginManagementInjector) {
227         this.pluginManagementInjector = pluginManagementInjector;
228         return this;
229     }
230 
231     public DefaultModelBuilder setReportConfigurationExpander(ReportConfigurationExpander reportConfigurationExpander) {
232         this.reportConfigurationExpander = reportConfigurationExpander;
233         return this;
234     }
235 
236     public DefaultModelBuilder setReportingConverter(ReportingConverter reportingConverter) {
237         this.reportingConverter = reportingConverter;
238         return this;
239     }
240 
241     public DefaultModelBuilder setProfileActivationFilePathInterpolator(
242             ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator) {
243         this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator;
244         return this;
245     }
246 
247     @SuppressWarnings("checkstyle:methodlength")
248     @Override
249     public ModelBuildingResult build(ModelBuildingRequest request) throws ModelBuildingException {
250         return build(request, new LinkedHashSet<>());
251     }
252 
253     @SuppressWarnings("checkstyle:methodlength")
254     protected ModelBuildingResult build(ModelBuildingRequest request, Collection<String> importIds)
255             throws ModelBuildingException {
256         // phase 1
257         DefaultModelBuildingResult result = new DefaultModelBuildingResult();
258 
259         DefaultModelProblemCollector problems = new DefaultModelProblemCollector(result);
260 
261         // read and validate raw model
262         Model inputModel = request.getRawModel();
263         if (inputModel == null) {
264             inputModel = readModel(request.getModelSource(), request.getPomFile(), request, problems);
265         }
266 
267         // profile activation
268         DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel);
269 
270         problems.setSource("(external profiles)");
271         List<Profile> activeExternalProfiles =
272                 profileSelector.getActiveProfiles(request.getProfiles(), profileActivationContext, problems);
273 
274         result.setActiveExternalProfiles(activeExternalProfiles);
275 
276         if (!activeExternalProfiles.isEmpty()) {
277             Properties profileProps = new Properties();
278             for (Profile profile : activeExternalProfiles) {
279                 profileProps.putAll(profile.getProperties());
280             }
281             profileProps.putAll(profileActivationContext.getUserProperties());
282             profileActivationContext.setUserProperties(profileProps);
283         }
284 
285         problems.setRootModel(inputModel);
286 
287         ModelData resultData = new ModelData(request.getModelSource(), inputModel);
288         ModelData superData = new ModelData(null, getSuperModel());
289 
290         Collection<String> parentIds = new LinkedHashSet<>();
291         List<ModelData> lineage = new ArrayList<>();
292 
293         // Models built for dependency resolution (a dependency POM, one of its parents, or an
294         // imported BOM) are read at ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL rather than
295         // the default STRICT level used for the project being built.
296         boolean externalModel = isExternalModelBuildingRequest(request);
297 
298         for (ModelData currentData = resultData; currentData != null; ) {
299             lineage.add(currentData);
300 
301             Model rawModel = currentData.getModel();
302             currentData.setRawModel(rawModel);
303 
304             Model tmpModel = rawModel.clone();
305             currentData.setModel(tmpModel);
306 
307             problems.setSource(tmpModel);
308 
309             // model normalization
310             modelNormalizer.mergeDuplicates(tmpModel, request, problems);
311 
312             profileActivationContext.setProjectProperties(tmpModel.getProperties());
313 
314             List<Profile> interpolatedProfiles = getInterpolatedProfiles(rawModel, profileActivationContext, problems);
315             tmpModel.setProfiles(interpolatedProfiles);
316 
317             List<Profile> profilesToEvaluate =
318                     externalModel ? withoutFileActivation(tmpModel.getProfiles()) : tmpModel.getProfiles();
319             List<Profile> activePomProfiles = profileSelector.getActiveProfiles(
320                     profilesToEvaluate,
321                     externalModel ? externalActivationContext(profileActivationContext) : profileActivationContext,
322                     problems);
323 
324             List<Profile> rawProfiles = new ArrayList<>();
325             for (Profile activePomProfile : activePomProfiles) {
326                 rawProfiles.add(rawModel.getProfiles().get(interpolatedProfiles.indexOf(activePomProfile)));
327             }
328             currentData.setActiveProfiles(rawProfiles);
329 
330             // profile injection
331             // TODO(#13146): repositories contributed by external-model profiles can shadow
332             // central; a WARN/FAIL policy for URL mismatches should be added separately.
333             for (Profile activeProfile : activePomProfiles) {
334                 profileInjector.injectProfile(tmpModel, activeProfile, request, problems);
335             }
336 
337             if (currentData == resultData) {
338                 for (Profile activeProfile : activeExternalProfiles) {
339                     profileInjector.injectProfile(tmpModel, activeProfile, request, problems);
340                 }
341             }
342 
343             if (currentData == superData) {
344                 break;
345             }
346 
347             configureResolver(request.getModelResolver(), tmpModel, problems);
348 
349             ModelData parentData = readParent(tmpModel, currentData.getSource(), request, problems);
350 
351             if (parentData == null) {
352                 currentData = superData;
353             } else if (currentData == resultData) { // First iteration - add initial id after version resolution.
354                 currentData.setGroupId(
355                         currentData.getRawModel().getGroupId() == null
356                                 ? parentData.getGroupId()
357                                 : currentData.getRawModel().getGroupId());
358 
359                 currentData.setVersion(
360                         currentData.getRawModel().getVersion() == null
361                                 ? parentData.getVersion()
362                                 : currentData.getRawModel().getVersion());
363 
364                 currentData.setArtifactId(currentData.getRawModel().getArtifactId());
365                 parentIds.add(currentData.getId());
366                 // Reset - only needed for 'getId'.
367                 currentData.setGroupId(null);
368                 currentData.setArtifactId(null);
369                 currentData.setVersion(null);
370                 currentData = parentData;
371             } else if (!parentIds.add(parentData.getId())) {
372                 StringBuilder message = new StringBuilder("The parents form a cycle: ");
373                 for (String modelId : parentIds) {
374                     message.append(modelId);
375                     message.append(" -> ");
376                 }
377                 message.append(parentData.getId());
378 
379                 problems.add(new ModelProblemCollectorRequest(ModelProblem.Severity.FATAL, ModelProblem.Version.BASE)
380                         .setMessage(message.toString()));
381 
382                 throw problems.newModelBuildingException();
383             } else {
384                 currentData = parentData;
385             }
386         }
387 
388         problems.setSource(inputModel);
389         checkPluginVersions(lineage, request, problems);
390 
391         // inheritance assembly
392         assembleInheritance(lineage, request, problems);
393 
394         Model resultModel = resultData.getModel();
395 
396         problems.setSource(resultModel);
397         problems.setRootModel(resultModel);
398 
399         // model interpolation
400         resultModel = interpolateModel(resultModel, request, problems);
401         resultData.setModel(resultModel);
402 
403         if (resultModel.getParent() != null) {
404             final ModelData parentData = lineage.get(1);
405             if (parentData.getVersion() == null || parentData.getVersion().contains("${")) {
406                 final Model interpolatedParent = interpolateModel(parentData.getModel(), request, problems);
407                 // parentData.setModel( interpolatedParent );
408                 parentData.setVersion(interpolatedParent.getVersion());
409             }
410         }
411 
412         // url normalization
413         modelUrlNormalizer.normalize(resultModel, request);
414 
415         // Now the fully interpolated model is available: reconfigure the resolver
416         configureResolver(request.getModelResolver(), resultModel, problems, true);
417 
418         resultData.setGroupId(resultModel.getGroupId());
419         resultData.setArtifactId(resultModel.getArtifactId());
420         resultData.setVersion(resultModel.getVersion());
421 
422         result.setEffectiveModel(resultModel);
423 
424         for (ModelData currentData : lineage) {
425             String modelId = (currentData != superData) ? currentData.getId() : "";
426 
427             result.addModelId(modelId);
428             result.setActivePomProfiles(modelId, currentData.getActiveProfiles());
429             result.setRawModel(modelId, currentData.getRawModel());
430         }
431 
432         if (!request.isTwoPhaseBuilding()) {
433             build(request, result, importIds);
434         }
435 
436         return result;
437     }
438 
439     @FunctionalInterface
440     private interface InterpolateString {
441         String apply(String s) throws InterpolationException;
442     }
443 
444     private List<Profile> getInterpolatedProfiles(
445             Model rawModel, DefaultProfileActivationContext context, DefaultModelProblemCollector problems) {
446         List<Profile> interpolatedActivations = getProfiles(rawModel);
447 
448         if (interpolatedActivations.isEmpty()) {
449             return Collections.emptyList();
450         }
451         RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
452 
453         interpolator.addValueSource(new MapBasedValueSource(context.getProjectProperties()));
454         interpolator.addValueSource(new MapBasedValueSource(context.getUserProperties()));
455         interpolator.addValueSource(new MapBasedValueSource(context.getSystemProperties()));
456 
457         class Interpolation {
458             final InputLocationTracker target;
459 
460             final InterpolateString impl;
461 
462             Interpolation(InputLocationTracker target, InterpolateString impl) {
463                 this.target = target;
464                 this.impl = impl;
465             }
466 
467             void performFor(String value, String locationKey, Consumer<String> mutator) {
468                 if (StringUtils.isEmpty(value)) {
469                     return;
470                 }
471                 try {
472                     mutator.accept(impl.apply(value));
473                 } catch (InterpolationException e) {
474                     problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
475                             .setMessage("Failed to interpolate value " + value + ": " + e.getMessage())
476                             .setLocation(target.getLocation(locationKey))
477                             .setException(e));
478                 }
479             }
480         }
481         for (Profile profile : interpolatedActivations) {
482             Activation activation = profile.getActivation();
483             Optional<Activation> a = Optional.ofNullable(activation);
484             a.map(Activation::getFile).ifPresent(fa -> {
485                 Interpolation nt =
486                         new Interpolation(fa, s -> profileActivationFilePathInterpolator.interpolate(s, context));
487                 nt.performFor(fa.getExists(), "exists", fa::setExists);
488                 nt.performFor(fa.getMissing(), "missing", fa::setMissing);
489             });
490             a.map(Activation::getOs).ifPresent(oa -> {
491                 Interpolation nt = new Interpolation(oa, interpolator::interpolate);
492                 nt.performFor(oa.getArch(), "arch", oa::setArch);
493                 nt.performFor(oa.getFamily(), "family", oa::setFamily);
494                 nt.performFor(oa.getName(), "name", oa::setName);
495                 nt.performFor(oa.getVersion(), "version", oa::setVersion);
496             });
497             a.map(Activation::getProperty).ifPresent(pa -> {
498                 Interpolation nt = new Interpolation(pa, interpolator::interpolate);
499                 nt.performFor(pa.getName(), "name", pa::setName);
500                 nt.performFor(pa.getValue(), "value", pa::setValue);
501             });
502             a.map(Activation::getJdk).ifPresent(ja -> new Interpolation(activation, interpolator::interpolate)
503                     .performFor(ja, "jdk", activation::setJdk));
504         }
505         return interpolatedActivations;
506     }
507 
508     /**
509      * Determines whether the given request builds a model for dependency resolution, i.e. a POM
510      * read from a remote repository (a dependency POM, one of its parents or an imported BOM)
511      * rather than a POM belonging to the project being built. Such requests are issued with
512      * {@link ModelBuildingRequest#VALIDATION_LEVEL_MINIMAL}, see for instance
513      * {@code DefaultArtifactDescriptorReader#loadPom}.
514      */
515     private static boolean isExternalModelBuildingRequest(ModelBuildingRequest request) {
516         return request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
517     }
518 
519     /**
520      * Returns a sandboxed {@link ProfileActivationContext} for evaluating profiles in
521      * repository-resolved (external) models — dependency POMs, parent POMs, and imported BOMs.
522      * <p>
523      * The sandboxed context preserves system properties (so JDK/OS activation works) and
524      * merges the POM's own {@code <properties>} into the system properties map so that
525      * property-activated profiles that depend on POM-declared values still work.
526      * User properties (consumer {@code -D} flags) are suppressed because they were not
527      * set for the dependency and must not accidentally activate its profiles.
528      * File-based profiles are pre-filtered via {@link #withoutFileActivation(List)} before
529      * reaching this context, so {@code getProjectDirectory()} is not relied on for file checks.
530      * <p>
531      * Model properties are merged into system properties (with system properties taking
532      * precedence) rather than changing the {@code PropertyProfileActivator} lookup chain,
533      * because changing the activator would affect ALL profile evaluations — including the
534      * build's own project — which can cause unintended profile activation when a POM declares
535      * a property that matches a profile's activation condition.
536      *
537      * @param delegate the original full context for this model build
538      * @return a sandboxed context suitable for external model profile activation
539      */
540     private static ProfileActivationContext externalActivationContext(ProfileActivationContext delegate) {
541         // Pre-compute the merged system+project properties once per external model.
542         // getSystemProperties() may be called multiple times per profile (e.g. OperatingSystemProfileActivator
543         // calls it 3× for name/arch/version), so allocating a new HashMap on every call is O(deps × profiles ×
544         // |systemProperties|). Computing it eagerly here keeps the anonymous class allocation-free.
545         final Map<String, String> mergedSystemProps;
546         Map<String, String> projectProps = delegate.getProjectProperties();
547         if (projectProps == null || projectProps.isEmpty()) {
548             mergedSystemProps = Collections.unmodifiableMap(delegate.getSystemProperties());
549         } else {
550             Map<String, String> merged = new HashMap<>(projectProps);
551             merged.putAll(delegate.getSystemProperties()); // system wins
552             mergedSystemProps = Collections.unmodifiableMap(merged);
553         }
554         return new ProfileActivationContext() {
555             @Override
556             public List<String> getActiveProfileIds() {
557                 return delegate.getActiveProfileIds();
558             }
559 
560             @Override
561             public List<String> getInactiveProfileIds() {
562                 return delegate.getInactiveProfileIds();
563             }
564 
565             /**
566              * System properties merged with project properties (system wins on conflict).
567              * This makes POM-declared properties visible to the PropertyProfileActivator
568              * without modifying the activator's lookup chain for non-external models.
569              */
570             @Override
571             public Map<String, String> getSystemProperties() {
572                 return mergedSystemProps;
573             }
574 
575             /** User properties are suppressed: consumer -D flags do not activate dependency profiles. */
576             @Override
577             public Map<String, String> getUserProperties() {
578                 return Collections.emptyMap();
579             }
580 
581             @Override
582             public Map<String, String> getProjectProperties() {
583                 return delegate.getProjectProperties();
584             }
585 
586             @Override
587             public File getProjectDirectory() {
588                 return delegate.getProjectDirectory();
589             }
590         };
591     }
592 
593     /**
594      * Returns the profiles from the given list whose activation does not depend on a file.
595      * File-activated profiles are suppressed in external model builds because publisher-local
596      * paths do not exist in the consumer's environment and resolving them against the consumer's
597      * filesystem would produce non-deterministic or incorrect results.
598      *
599      * @param profiles the full profile list
600      * @return profiles with file-activated ones removed
601      */
602     private static List<Profile> withoutFileActivation(List<Profile> profiles) {
603         List<Profile> eligible = new ArrayList<>(profiles.size());
604         for (Profile profile : profiles) {
605             Activation activation = profile.getActivation();
606             if (activation == null || activation.getFile() == null) {
607                 eligible.add(profile);
608             }
609         }
610         return eligible;
611     }
612 
613     @Override
614     public ModelBuildingResult build(ModelBuildingRequest request, ModelBuildingResult result)
615             throws ModelBuildingException {
616         return build(request, result, new LinkedHashSet());
617     }
618 
619     private ModelBuildingResult build(
620             ModelBuildingRequest request, ModelBuildingResult result, Collection<String> imports)
621             throws ModelBuildingException {
622         // phase 2
623         Model resultModel = result.getEffectiveModel();
624 
625         DefaultModelProblemCollector problems = new DefaultModelProblemCollector(result);
626         problems.setSource(resultModel);
627         problems.setRootModel(resultModel);
628 
629         // model path translation
630         modelPathTranslator.alignToBaseDirectory(resultModel, resultModel.getProjectDirectory(), request);
631 
632         // plugin management injection
633         pluginManagementInjector.injectManagement(resultModel, request, problems);
634 
635         fireEvent(resultModel, request, problems, ModelBuildingEventCatapult.BUILD_EXTENSIONS_ASSEMBLED);
636 
637         if (request.isProcessPlugins()) {
638             if (lifecycleBindingsInjector == null) {
639                 throw new IllegalStateException("lifecycle bindings injector is missing");
640             }
641 
642             // lifecycle bindings injection
643             lifecycleBindingsInjector.injectLifecycleBindings(resultModel, request, problems);
644         }
645 
646         // dependency management import
647         importDependencyManagement(resultModel, request, problems, imports);
648 
649         // dependency management injection
650         dependencyManagementInjector.injectManagement(resultModel, request, problems);
651 
652         modelNormalizer.injectDefaultValues(resultModel, request, problems);
653 
654         if (request.isProcessPlugins()) {
655             // reports configuration
656             reportConfigurationExpander.expandPluginConfiguration(resultModel, request, problems);
657 
658             // reports conversion to decoupled site plugin
659             reportingConverter.convertReporting(resultModel, request, problems);
660 
661             // plugins configuration
662             pluginConfigurationExpander.expandPluginConfiguration(resultModel, request, problems);
663         }
664 
665         // effective model validation
666         modelValidator.validateEffectiveModel(resultModel, request, problems);
667 
668         if (hasModelErrors(problems)) {
669             throw problems.newModelBuildingException();
670         }
671 
672         return result;
673     }
674 
675     @Override
676     public Result<? extends Model> buildRawModel(File pomFile, int validationLevel, boolean locationTracking) {
677         final ModelBuildingRequest request = new DefaultModelBuildingRequest()
678                 .setValidationLevel(validationLevel)
679                 .setLocationTracking(locationTracking);
680         final DefaultModelProblemCollector collector =
681                 new DefaultModelProblemCollector(new DefaultModelBuildingResult());
682         try {
683             return newResult(readModel(null, pomFile, request, collector), collector.getProblems());
684         } catch (ModelBuildingException e) {
685             return error(collector.getProblems());
686         }
687     }
688 
689     private Model readModel(
690             ModelSource modelSource, File pomFile, ModelBuildingRequest request, DefaultModelProblemCollector problems)
691             throws ModelBuildingException {
692         Model model;
693 
694         if (modelSource == null) {
695             if (pomFile != null) {
696                 modelSource = new FileModelSource(pomFile);
697             } else {
698                 throw new NullPointerException("neither pomFile nor modelSource can be null");
699             }
700         }
701 
702         problems.setSource(modelSource.getLocation());
703         try {
704             boolean strict = request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
705             InputSource source = request.isLocationTracking() ? new InputSource() : null;
706 
707             Map<String, Object> options = new HashMap<>();
708             options.put(ModelProcessor.IS_STRICT, strict);
709             options.put(ModelProcessor.INPUT_SOURCE, source);
710             options.put(ModelProcessor.SOURCE, modelSource);
711 
712             try {
713                 model = modelProcessor.read(modelSource.getInputStream(), options);
714             } catch (ModelParseException e) {
715                 if (!strict) {
716                     throw e;
717                 }
718 
719                 options.put(ModelProcessor.IS_STRICT, Boolean.FALSE);
720 
721                 try {
722                     model = modelProcessor.read(modelSource.getInputStream(), options);
723                 } catch (ModelParseException ne) {
724                     // still unreadable even in non-strict mode, rethrow original error
725                     throw e;
726                 }
727 
728                 if (pomFile != null) {
729                     problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.V20)
730                             .setMessage("Malformed POM " + modelSource.getLocation() + ": " + e.getMessage())
731                             .setException(e));
732                 } else {
733                     problems.add(new ModelProblemCollectorRequest(Severity.WARNING, Version.V20)
734                             .setMessage("Malformed POM " + modelSource.getLocation() + ": " + e.getMessage())
735                             .setException(e));
736                 }
737             }
738 
739             if (source != null) {
740                 source.setModelId(ModelProblemUtils.toId(model));
741                 source.setLocation(modelSource.getLocation());
742             }
743         } catch (ModelParseException e) {
744             problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
745                     .setMessage("Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage())
746                     .setException(e));
747             throw problems.newModelBuildingException();
748         } catch (IOException e) {
749             String msg = e.getMessage();
750             if (msg == null || msg.length() <= 0) {
751                 // NOTE: There's java.nio.charset.MalformedInputException and sun.io.MalformedInputException
752                 if (e.getClass().getName().endsWith("MalformedInputException")) {
753                     msg = "Some input bytes do not match the file encoding.";
754                 } else {
755                     msg = e.getClass().getSimpleName();
756                 }
757             }
758             problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
759                     .setMessage("Non-readable POM " + modelSource.getLocation() + ": " + msg)
760                     .setException(e));
761             throw problems.newModelBuildingException();
762         }
763 
764         model.setPomFile(pomFile);
765 
766         problems.setSource(model);
767         modelValidator.validateRawModel(model, request, problems);
768 
769         if (hasFatalErrors(problems)) {
770             throw problems.newModelBuildingException();
771         }
772 
773         return model;
774     }
775 
776     private DefaultProfileActivationContext getProfileActivationContext(ModelBuildingRequest request, Model rawModel) {
777         DefaultProfileActivationContext context = new DefaultProfileActivationContext();
778 
779         context.setActiveProfileIds(request.getActiveProfileIds());
780         context.setInactiveProfileIds(request.getInactiveProfileIds());
781         context.setSystemProperties(request.getSystemProperties());
782         // enrich user properties with project packaging
783         Properties userProperties = request.getUserProperties();
784         userProperties.computeIfAbsent(
785                 (Object) ProfileActivationContext.PROPERTY_NAME_PACKAGING, (p) -> (Object) rawModel.getPackaging());
786         context.setUserProperties(userProperties);
787         context.setProjectDirectory(
788                 (request.getPomFile() != null) ? request.getPomFile().getParentFile() : null);
789 
790         return context;
791     }
792 
793     private void configureResolver(ModelResolver modelResolver, Model model, DefaultModelProblemCollector problems) {
794         configureResolver(modelResolver, model, problems, false);
795     }
796 
797     private void configureResolver(
798             ModelResolver modelResolver,
799             Model model,
800             DefaultModelProblemCollector problems,
801             boolean replaceRepositories) {
802         if (modelResolver == null) {
803             return;
804         }
805 
806         problems.setSource(model);
807 
808         List<Repository> repositories = model.getRepositories();
809 
810         for (Repository repository : repositories) {
811             try {
812                 modelResolver.addRepository(repository, replaceRepositories);
813             } catch (InvalidRepositoryException e) {
814                 problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
815                         .setMessage("Invalid repository " + repository.getId() + ": " + e.getMessage())
816                         .setLocation(repository.getLocation(""))
817                         .setException(e));
818             }
819         }
820     }
821 
822     private void checkPluginVersions(
823             List<ModelData> lineage, ModelBuildingRequest request, ModelProblemCollector problems) {
824         if (request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
825             return;
826         }
827 
828         Map<String, Plugin> plugins = new HashMap<>();
829         Map<String, String> versions = new HashMap<>();
830         Map<String, String> managedVersions = new HashMap<>();
831 
832         for (int i = lineage.size() - 1; i >= 0; i--) {
833             Model model = lineage.get(i).getModel();
834             Build build = model.getBuild();
835             if (build != null) {
836                 for (Plugin plugin : build.getPlugins()) {
837                     String key = plugin.getKey();
838                     if (versions.get(key) == null) {
839                         versions.put(key, plugin.getVersion());
840                         plugins.put(key, plugin);
841                     }
842                 }
843                 PluginManagement mgmt = build.getPluginManagement();
844                 if (mgmt != null) {
845                     for (Plugin plugin : mgmt.getPlugins()) {
846                         String key = plugin.getKey();
847                         if (managedVersions.get(key) == null) {
848                             managedVersions.put(key, plugin.getVersion());
849                         }
850                     }
851                 }
852             }
853         }
854 
855         for (String key : versions.keySet()) {
856             if (versions.get(key) == null && managedVersions.get(key) == null) {
857                 InputLocation location = plugins.get(key).getLocation("");
858                 problems.add(new ModelProblemCollectorRequest(Severity.WARNING, Version.V20)
859                         .setMessage("'build.plugins.plugin.version' for " + key + " is missing.")
860                         .setLocation(location));
861             }
862         }
863     }
864 
865     private void assembleInheritance(
866             List<ModelData> lineage, ModelBuildingRequest request, ModelProblemCollector problems) {
867         for (int i = lineage.size() - 2; i >= 0; i--) {
868             Model parent = lineage.get(i + 1).getModel();
869             Model child = lineage.get(i).getModel();
870             inheritanceAssembler.assembleModelInheritance(child, parent, request, problems);
871         }
872     }
873 
874     private List<Profile> getProfiles(Model model) {
875         ArrayList<Profile> profiles = new ArrayList<>();
876         for (Profile profile : model.getProfiles()) {
877             profile = profile.clone();
878             profiles.add(profile);
879         }
880         return profiles;
881     }
882 
883     private Model interpolateModel(Model model, ModelBuildingRequest request, ModelProblemCollector problems) {
884         // save profile activations before interpolation, since they are evaluated with limited scope
885         List<Profile> originalProfiles = getProfiles(model);
886 
887         Model interpolatedModel =
888                 modelInterpolator.interpolateModel(model, model.getProjectDirectory(), request, problems);
889         if (interpolatedModel.getParent() != null) {
890             StringSearchInterpolator ssi = new StringSearchInterpolator();
891             ssi.addValueSource(new MapBasedValueSource(request.getUserProperties()));
892 
893             ssi.addValueSource(new MapBasedValueSource(model.getProperties()));
894 
895             ssi.addValueSource(new MapBasedValueSource(request.getSystemProperties()));
896 
897             try {
898                 String interpolated =
899                         ssi.interpolate(interpolatedModel.getParent().getVersion());
900                 interpolatedModel.getParent().setVersion(interpolated);
901             } catch (Exception e) {
902                 ModelProblemCollectorRequest mpcr = new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
903                         .setMessage("Failed to interpolate field: "
904                                 + interpolatedModel.getParent().getVersion()
905                                 + " on class: ")
906                         .setException(e);
907                 problems.add(mpcr);
908             }
909         }
910         interpolatedModel.setPomFile(model.getPomFile());
911 
912         // restore profiles with any activation to their value before full interpolation
913         List<Profile> interpolatedProfiles = model.getProfiles();
914         IntStream.range(0, interpolatedProfiles.size()).forEach(i -> interpolatedProfiles
915                 .get(i)
916                 .setActivation(originalProfiles.get(i).getActivation()));
917 
918         return interpolatedModel;
919     }
920 
921     private ModelData readParent(
922             Model childModel,
923             ModelSource childSource,
924             ModelBuildingRequest request,
925             DefaultModelProblemCollector problems)
926             throws ModelBuildingException {
927         ModelData parentData;
928 
929         Parent parent = childModel.getParent();
930 
931         if (parent != null) {
932             String groupId = parent.getGroupId();
933             String artifactId = parent.getArtifactId();
934             String version = parent.getVersion();
935 
936             parentData = getCache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW);
937 
938             if (parentData == null) {
939                 parentData = readParentLocally(childModel, childSource, request, problems);
940 
941                 if (parentData == null) {
942                     parentData = readParentExternally(childModel, request, problems);
943                 }
944 
945                 putCache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.RAW, parentData);
946             } else {
947                 /*
948                  * NOTE: This is a sanity check of the cache hit. If the cached parent POM was locally resolved, the
949                  * child's <relativePath> should point at that parent, too. If it doesn't, we ignore the cache and
950                  * resolve externally, to mimic the behavior if the cache didn't exist in the first place. Otherwise,
951                  * the cache would obscure a bad POM.
952                  */
953 
954                 File pomFile = parentData.getModel().getPomFile();
955                 if (pomFile != null) {
956                     FileModelSource pomSource = new FileModelSource(pomFile);
957                     ModelSource expectedParentSource = getParentPomFile(childModel, childSource);
958 
959                     if (expectedParentSource == null
960                             || (expectedParentSource instanceof ModelSource2
961                                     && !pomSource.equals(expectedParentSource))) {
962                         parentData = readParentExternally(childModel, request, problems);
963                     }
964                 }
965             }
966 
967             Model parentModel = parentData.getModel();
968 
969             if (!"pom".equals(parentModel.getPackaging())) {
970                 problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
971                         .setMessage("Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint(parentModel)
972                                 + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"")
973                         .setLocation(parentModel.getLocation("packaging")));
974             }
975         } else {
976             parentData = null;
977         }
978 
979         return parentData;
980     }
981 
982     private ModelData readParentLocally(
983             Model childModel,
984             ModelSource childSource,
985             ModelBuildingRequest request,
986             DefaultModelProblemCollector problems)
987             throws ModelBuildingException {
988         final Parent parent = childModel.getParent();
989         final ModelSource candidateSource;
990         final Model candidateModel;
991         final WorkspaceModelResolver resolver = request.getWorkspaceModelResolver();
992         if (resolver == null) {
993             candidateSource = getParentPomFile(childModel, childSource);
994 
995             if (candidateSource == null) {
996                 return null;
997             }
998 
999             File pomFile = null;
1000             if (candidateSource instanceof FileModelSource source) {
1001                 pomFile = source.getPomFile();
1002             }
1003 
1004             candidateModel = readModel(candidateSource, pomFile, request, problems);
1005         } else {
1006             try {
1007                 candidateModel =
1008                         resolver.resolveRawModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
1009             } catch (UnresolvableModelException e) {
1010                 problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE) //
1011                         .setMessage(e.getMessage())
1012                         .setLocation(parent.getLocation(""))
1013                         .setException(e));
1014                 throw problems.newModelBuildingException();
1015             }
1016             if (candidateModel == null) {
1017                 return null;
1018             }
1019             candidateSource = new FileModelSource(candidateModel.getPomFile());
1020         }
1021 
1022         //
1023         // TODO jvz Why isn't all this checking the job of the duty of the workspace resolver, we know that we
1024         // have a model that is suitable, yet more checks are done here and the one for the version is problematic
1025         // before because with parents as ranges it will never work in this scenario.
1026         //
1027 
1028         String groupId = candidateModel.getGroupId();
1029         if (groupId == null && candidateModel.getParent() != null) {
1030             groupId = candidateModel.getParent().getGroupId();
1031         }
1032         String artifactId = candidateModel.getArtifactId();
1033         String version = candidateModel.getVersion();
1034         if (version == null && candidateModel.getParent() != null) {
1035             version = candidateModel.getParent().getVersion();
1036         }
1037 
1038         if (groupId == null
1039                 || !groupId.equals(parent.getGroupId())
1040                 || artifactId == null
1041                 || !artifactId.equals(parent.getArtifactId())) {
1042             String actual = groupId + ':' + artifactId;
1043             String declared = parent.getGroupId() + ':' + parent.getArtifactId();
1044             String sourceHint =
1045                     (childModel != problems.getRootModel()) ? ModelProblemUtils.toSourceHint(childModel) : null;
1046 
1047             String message;
1048             if (parent.getRelativePath() == null) {
1049                 // <relativePath> was omitted — Maven probed ../pom.xml on its own
1050                 message = "Maven probed the default location '../pom.xml'"
1051                         + (sourceHint != null ? " for POM " + sourceHint : "")
1052                         + " and found " + actual
1053                         + " instead of the declared parent " + declared
1054                         + ". Maven will fall back to repository resolution."
1055                         + " To suppress this warning, add <relativePath/> to your <parent> declaration.";
1056             } else {
1057                 // <relativePath> was set explicitly
1058                 message = "'parent.relativePath'"
1059                         + (sourceHint != null ? " of POM " + sourceHint : "")
1060                         + " points at '" + parent.getRelativePath() + "'"
1061                         + " which resolves to " + actual
1062                         + " instead of the declared parent " + declared
1063                         + ". Please verify your project structure or correct the <relativePath> value.";
1064             }
1065 
1066             problems.setSource(childModel);
1067             problems.add(new ModelProblemCollectorRequest(Severity.WARNING, Version.BASE)
1068                     .setMessage(message)
1069                     .setLocation(parent.getLocation("")));
1070             return null;
1071         }
1072         if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) {
1073             try {
1074                 VersionRange parentRange = VersionRange.createFromVersionSpec(parent.getVersion());
1075                 if (!parentRange.hasRestrictions()) {
1076                     // the parent version is not a range, we have version skew, drop back to resolution from repo
1077                     return null;
1078                 }
1079                 if (!parentRange.containsVersion(new DefaultArtifactVersion(version))) {
1080                     // version skew drop back to resolution from the repository
1081                     return null;
1082                 }
1083 
1084                 // Validate versions aren't inherited when using parent ranges the same way as when read externally.
1085                 String rawChildModelVersion = childModel.getVersion();
1086 
1087                 if (rawChildModelVersion == null) {
1088                     // Message below is checked for in the MNG-2199 core IT.
1089                     problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V31)
1090                             .setMessage("Version must be a constant")
1091                             .setLocation(childModel.getLocation("")));
1092 
1093                 } else {
1094                     if (rawChildVersionReferencesParent(rawChildModelVersion)) {
1095                         // Message below is checked for in the MNG-2199 core IT.
1096                         problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V31)
1097                                 .setMessage("Version must be a constant")
1098                                 .setLocation(childModel.getLocation("version")));
1099                     }
1100                 }
1101 
1102                 // MNG-2199: What else to check here ?
1103             } catch (InvalidVersionSpecificationException e) {
1104                 // invalid version range, so drop back to resolution from the repository
1105                 return null;
1106             }
1107         }
1108 
1109         //
1110         // Here we just need to know that a version is fine to use but this validation we can do in our workspace
1111         // resolver.
1112         //
1113 
1114         /*
1115          * if ( version == null || !version.equals( parent.getVersion() ) ) { return null; }
1116          */
1117 
1118         ModelData parentData = new ModelData(candidateSource, candidateModel, groupId, artifactId, version);
1119 
1120         return parentData;
1121     }
1122 
1123     private boolean rawChildVersionReferencesParent(String rawChildModelVersion) {
1124         return rawChildModelVersion.equals("${pom.version}")
1125                 || rawChildModelVersion.equals("${project.version}")
1126                 || rawChildModelVersion.equals("${pom.parent.version}")
1127                 || rawChildModelVersion.equals("${project.parent.version}");
1128     }
1129 
1130     private ModelSource getParentPomFile(Model childModel, ModelSource source) {
1131         if (!(source instanceof ModelSource2)) {
1132             return null;
1133         }
1134 
1135         String parentPath = childModel.getParent().getRelativePath();
1136         if (parentPath == null) {
1137             parentPath = "../pom.xml";
1138         }
1139 
1140         if (parentPath.length() <= 0) {
1141             return null;
1142         }
1143 
1144         return ((ModelSource2) source).getRelatedSource(parentPath);
1145     }
1146 
1147     private ModelData readParentExternally(
1148             Model childModel, ModelBuildingRequest request, DefaultModelProblemCollector problems)
1149             throws ModelBuildingException {
1150         problems.setSource(childModel);
1151 
1152         Parent parent = childModel.getParent().clone();
1153 
1154         String groupId = parent.getGroupId();
1155         String artifactId = parent.getArtifactId();
1156         String version = parent.getVersion();
1157 
1158         ModelResolver modelResolver = request.getModelResolver();
1159         Objects.requireNonNull(
1160                 modelResolver,
1161                 String.format(
1162                         "request.modelResolver cannot be null (parent POM %s and POM %s)",
1163                         ModelProblemUtils.toId(groupId, artifactId, version),
1164                         ModelProblemUtils.toSourceHint(childModel)));
1165 
1166         ModelSource modelSource;
1167         try {
1168             modelSource = modelResolver.resolveModel(parent);
1169         } catch (UnresolvableModelException e) {
1170             // Message below is checked for in the MNG-2199 core IT.
1171             StringBuilder buffer = new StringBuilder(256);
1172             buffer.append("Non-resolvable parent POM");
1173             if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
1174                 buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
1175             }
1176             if (childModel != problems.getRootModel()) {
1177                 buffer.append(" for ").append(ModelProblemUtils.toId(childModel));
1178             }
1179             buffer.append(": ").append(e.getMessage());
1180             if (childModel.getProjectDirectory() != null) {
1181                 if (parent.getRelativePath() == null || parent.getRelativePath().length() <= 0) {
1182                     buffer.append(" and 'parent.relativePath' points at no local POM");
1183                 } else {
1184                     buffer.append(" and 'parent.relativePath' points at wrong local POM");
1185                 }
1186             }
1187 
1188             problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
1189                     .setMessage(buffer.toString())
1190                     .setLocation(parent.getLocation(""))
1191                     .setException(e));
1192             throw problems.newModelBuildingException();
1193         }
1194 
1195         ModelBuildingRequest lenientRequest = request;
1196         if (request.getValidationLevel() > ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
1197             lenientRequest = new FilterModelBuildingRequest(request) {
1198                 @Override
1199                 public int getValidationLevel() {
1200                     return ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
1201                 }
1202             };
1203         }
1204 
1205         Model parentModel = readModel(modelSource, null, lenientRequest, problems);
1206 
1207         if (!parent.getVersion().equals(version)) {
1208             String rawChildModelVersion = childModel.getVersion();
1209 
1210             if (rawChildModelVersion == null) {
1211                 // Message below is checked for in the MNG-2199 core IT.
1212                 problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V31)
1213                         .setMessage("Version must be a constant")
1214                         .setLocation(childModel.getLocation("")));
1215 
1216             } else {
1217                 if (rawChildVersionReferencesParent(rawChildModelVersion)) {
1218                     // Message below is checked for in the MNG-2199 core IT.
1219                     problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.V31)
1220                             .setMessage("Version must be a constant")
1221                             .setLocation(childModel.getLocation("version")));
1222                 }
1223             }
1224 
1225             // MNG-2199: What else to check here ?
1226         }
1227 
1228         ModelData parentData = new ModelData(
1229                 modelSource, parentModel, parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
1230 
1231         return parentData;
1232     }
1233 
1234     private Model getSuperModel() {
1235         return superPomProvider.getSuperModel("4.0.0").clone();
1236     }
1237 
1238     @SuppressWarnings("checkstyle:methodlength")
1239     private void importDependencyManagement(
1240             Model model,
1241             ModelBuildingRequest request,
1242             DefaultModelProblemCollector problems,
1243             Collection<String> importIds) {
1244         DependencyManagement depMgmt = model.getDependencyManagement();
1245 
1246         if (depMgmt == null) {
1247             return;
1248         }
1249 
1250         String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
1251 
1252         importIds.add(importing);
1253 
1254         final WorkspaceModelResolver workspaceResolver = request.getWorkspaceModelResolver();
1255         final ModelResolver modelResolver = request.getModelResolver();
1256 
1257         ModelBuildingRequest importRequest = null;
1258 
1259         List<DependencyManagement> importMgmts = null;
1260 
1261         for (Iterator<Dependency> it = depMgmt.getDependencies().iterator(); it.hasNext(); ) {
1262             Dependency dependency = it.next();
1263 
1264             if (!"pom".equals(dependency.getType()) || !"import".equals(dependency.getScope())) {
1265                 continue;
1266             }
1267 
1268             it.remove();
1269 
1270             String groupId = dependency.getGroupId();
1271             String artifactId = dependency.getArtifactId();
1272             String version = dependency.getVersion();
1273 
1274             if (groupId == null || groupId.length() <= 0) {
1275                 problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
1276                         .setMessage("'dependencyManagement.dependencies.dependency.groupId' for "
1277                                 + dependency.getManagementKey() + " is missing.")
1278                         .setLocation(dependency.getLocation("")));
1279                 continue;
1280             }
1281             if (artifactId == null || artifactId.length() <= 0) {
1282                 problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
1283                         .setMessage("'dependencyManagement.dependencies.dependency.artifactId' for "
1284                                 + dependency.getManagementKey() + " is missing.")
1285                         .setLocation(dependency.getLocation("")));
1286                 continue;
1287             }
1288             if (version == null || version.length() <= 0) {
1289                 problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
1290                         .setMessage("'dependencyManagement.dependencies.dependency.version' for "
1291                                 + dependency.getManagementKey() + " is missing.")
1292                         .setLocation(dependency.getLocation("")));
1293                 continue;
1294             }
1295 
1296             String imported = groupId + ':' + artifactId + ':' + version;
1297 
1298             if (importIds.contains(imported)) {
1299                 StringBuilder message =
1300                         new StringBuilder("The dependencies of type=pom and with scope=import form a cycle: ");
1301                 for (String modelId : importIds) {
1302                     message.append(modelId);
1303                     message.append(" -> ");
1304                 }
1305                 message.append(imported);
1306                 problems.add(
1307                         new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE).setMessage(message.toString()));
1308 
1309                 continue;
1310             }
1311 
1312             DependencyManagement importMgmt =
1313                     getCache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT);
1314 
1315             if (importMgmt == null) {
1316                 if (workspaceResolver == null && modelResolver == null) {
1317                     throw new NullPointerException(String.format(
1318                             "request.workspaceModelResolver and request.modelResolver cannot be null"
1319                                     + " (parent POM %s and POM %s)",
1320                             ModelProblemUtils.toId(groupId, artifactId, version),
1321                             ModelProblemUtils.toSourceHint(model)));
1322                 }
1323 
1324                 Model importModel = null;
1325                 if (workspaceResolver != null) {
1326                     try {
1327                         importModel = workspaceResolver.resolveEffectiveModel(groupId, artifactId, version);
1328                     } catch (UnresolvableModelException e) {
1329                         problems.add(new ModelProblemCollectorRequest(Severity.FATAL, Version.BASE)
1330                                 .setMessage(e.getMessage())
1331                                 .setException(e));
1332                         continue;
1333                     }
1334                 }
1335 
1336                 // no workspace resolver or workspace resolver returned null (i.e. model not in workspace)
1337                 if (importModel == null) {
1338                     final ModelSource importSource;
1339                     try {
1340                         importSource = modelResolver.resolveModel(groupId, artifactId, version);
1341                     } catch (UnresolvableModelException e) {
1342                         StringBuilder buffer = new StringBuilder(256);
1343                         buffer.append("Non-resolvable import POM");
1344                         if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
1345                             buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
1346                         }
1347                         buffer.append(": ").append(e.getMessage());
1348 
1349                         problems.add(new ModelProblemCollectorRequest(Severity.ERROR, Version.BASE)
1350                                 .setMessage(buffer.toString())
1351                                 .setLocation(dependency.getLocation(""))
1352                                 .setException(e));
1353                         continue;
1354                     }
1355 
1356                     if (importRequest == null) {
1357                         importRequest = new DefaultModelBuildingRequest();
1358                         importRequest.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
1359                         importRequest.setModelCache(request.getModelCache());
1360                         importRequest.setSystemProperties(request.getSystemProperties());
1361                         importRequest.setUserProperties(request.getUserProperties());
1362                         importRequest.setLocationTracking(request.isLocationTracking());
1363                     }
1364 
1365                     importRequest.setModelSource(importSource);
1366                     importRequest.setModelResolver(modelResolver.newCopy());
1367 
1368                     final ModelBuildingResult importResult;
1369                     try {
1370                         importResult = build(importRequest, importIds);
1371                     } catch (ModelBuildingException e) {
1372                         problems.addAll(e.getProblems());
1373                         continue;
1374                     }
1375 
1376                     problems.addAll(importResult.getProblems());
1377 
1378                     importModel = importResult.getEffectiveModel();
1379                 }
1380 
1381                 importMgmt = importModel.getDependencyManagement();
1382 
1383                 if (importMgmt == null) {
1384                     importMgmt = new DependencyManagement();
1385                 }
1386 
1387                 putCache(request.getModelCache(), groupId, artifactId, version, ModelCacheTag.IMPORT, importMgmt);
1388             }
1389 
1390             if (importMgmts == null) {
1391                 importMgmts = new ArrayList<>();
1392             }
1393 
1394             importMgmts.add(importMgmt);
1395         }
1396 
1397         importIds.remove(importing);
1398 
1399         dependencyManagementImporter.importManagement(model, importMgmts, request, problems);
1400     }
1401 
1402     private <T> void putCache(
1403             ModelCache modelCache, String groupId, String artifactId, String version, ModelCacheTag<T> tag, T data) {
1404         if (modelCache != null) {
1405             modelCache.put(groupId, artifactId, version, tag.getName(), tag.intoCache(data));
1406         }
1407     }
1408 
1409     private <T> T getCache(
1410             ModelCache modelCache, String groupId, String artifactId, String version, ModelCacheTag<T> tag) {
1411         if (modelCache != null) {
1412             Object data = modelCache.get(groupId, artifactId, version, tag.getName());
1413             if (data != null) {
1414                 return tag.fromCache(tag.getType().cast(data));
1415             }
1416         }
1417         return null;
1418     }
1419 
1420     private void fireEvent(
1421             Model model,
1422             ModelBuildingRequest request,
1423             ModelProblemCollector problems,
1424             ModelBuildingEventCatapult catapult) {
1425         ModelBuildingListener listener = request.getModelBuildingListener();
1426 
1427         if (listener != null) {
1428             ModelBuildingEvent event = new DefaultModelBuildingEvent(model, request, problems);
1429 
1430             catapult.fire(listener, event);
1431         }
1432     }
1433 
1434     private boolean containsCoordinates(String message, String groupId, String artifactId, String version) {
1435         return message != null
1436                 && (groupId == null || message.contains(groupId))
1437                 && (artifactId == null || message.contains(artifactId))
1438                 && (version == null || message.contains(version));
1439     }
1440 
1441     protected boolean hasModelErrors(ModelProblemCollectorExt problems) {
1442         if (problems instanceof DefaultModelProblemCollector collector) {
1443             return collector.hasErrors();
1444         } else {
1445             // the default execution path only knows the DefaultModelProblemCollector,
1446             // only reason it's not in signature is because it's package private
1447             throw new IllegalStateException();
1448         }
1449     }
1450 
1451     protected boolean hasFatalErrors(ModelProblemCollectorExt problems) {
1452         if (problems instanceof DefaultModelProblemCollector collector) {
1453             return collector.hasFatalErrors();
1454         } else {
1455             // the default execution path only knows the DefaultModelProblemCollector,
1456             // only reason it's not in signature is because it's package private
1457             throw new IllegalStateException();
1458         }
1459     }
1460 }