1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.impl.model;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.lang.reflect.Field;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.Paths;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.LinkedHashMap;
35 import java.util.LinkedHashSet;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Objects;
39 import java.util.Set;
40 import java.util.concurrent.ConcurrentHashMap;
41 import java.util.concurrent.CopyOnWriteArrayList;
42 import java.util.concurrent.Executor;
43 import java.util.concurrent.Executors;
44 import java.util.concurrent.atomic.AtomicReference;
45 import java.util.function.BiFunction;
46 import java.util.function.Supplier;
47 import java.util.function.UnaryOperator;
48 import java.util.stream.Collectors;
49 import java.util.stream.Stream;
50
51 import org.apache.maven.api.Constants;
52 import org.apache.maven.api.RemoteRepository;
53 import org.apache.maven.api.Session;
54 import org.apache.maven.api.Type;
55 import org.apache.maven.api.VersionRange;
56 import org.apache.maven.api.annotations.Nonnull;
57 import org.apache.maven.api.annotations.Nullable;
58 import org.apache.maven.api.cache.CacheMetadata;
59 import org.apache.maven.api.cache.CacheRetention;
60 import org.apache.maven.api.di.Inject;
61 import org.apache.maven.api.di.Named;
62 import org.apache.maven.api.di.Singleton;
63 import org.apache.maven.api.feature.Features;
64 import org.apache.maven.api.model.Activation;
65 import org.apache.maven.api.model.Dependency;
66 import org.apache.maven.api.model.DependencyManagement;
67 import org.apache.maven.api.model.DeploymentRepository;
68 import org.apache.maven.api.model.DistributionManagement;
69 import org.apache.maven.api.model.Exclusion;
70 import org.apache.maven.api.model.InputLocation;
71 import org.apache.maven.api.model.InputSource;
72 import org.apache.maven.api.model.Model;
73 import org.apache.maven.api.model.Parent;
74 import org.apache.maven.api.model.Profile;
75 import org.apache.maven.api.model.Repository;
76 import org.apache.maven.api.services.BuilderProblem;
77 import org.apache.maven.api.services.BuilderProblem.Severity;
78 import org.apache.maven.api.services.Interpolator;
79 import org.apache.maven.api.services.MavenException;
80 import org.apache.maven.api.services.ModelBuilder;
81 import org.apache.maven.api.services.ModelBuilderException;
82 import org.apache.maven.api.services.ModelBuilderRequest;
83 import org.apache.maven.api.services.ModelBuilderResult;
84 import org.apache.maven.api.services.ModelProblem;
85 import org.apache.maven.api.services.ModelProblem.Version;
86 import org.apache.maven.api.services.ModelProblemCollector;
87 import org.apache.maven.api.services.ModelSource;
88 import org.apache.maven.api.services.ProblemCollector;
89 import org.apache.maven.api.services.RepositoryFactory;
90 import org.apache.maven.api.services.Request;
91 import org.apache.maven.api.services.RequestTrace;
92 import org.apache.maven.api.services.Result;
93 import org.apache.maven.api.services.Source;
94 import org.apache.maven.api.services.Sources;
95 import org.apache.maven.api.services.SuperPomProvider;
96 import org.apache.maven.api.services.VersionParserException;
97 import org.apache.maven.api.services.model.DependencyManagementImporter;
98 import org.apache.maven.api.services.model.DependencyManagementInjector;
99 import org.apache.maven.api.services.model.InheritanceAssembler;
100 import org.apache.maven.api.services.model.ModelInterpolator;
101 import org.apache.maven.api.services.model.ModelNormalizer;
102 import org.apache.maven.api.services.model.ModelPathTranslator;
103 import org.apache.maven.api.services.model.ModelProcessor;
104 import org.apache.maven.api.services.model.ModelResolver;
105 import org.apache.maven.api.services.model.ModelResolverException;
106 import org.apache.maven.api.services.model.ModelUrlNormalizer;
107 import org.apache.maven.api.services.model.ModelValidator;
108 import org.apache.maven.api.services.model.ModelVersionParser;
109 import org.apache.maven.api.services.model.PathTranslator;
110 import org.apache.maven.api.services.model.PluginConfigurationExpander;
111 import org.apache.maven.api.services.model.PluginManagementInjector;
112 import org.apache.maven.api.services.model.ProfileInjector;
113 import org.apache.maven.api.services.model.ProfileSelector;
114 import org.apache.maven.api.services.model.RootLocator;
115 import org.apache.maven.api.services.xml.XmlReaderException;
116 import org.apache.maven.api.services.xml.XmlReaderRequest;
117 import org.apache.maven.api.spi.ModelParserException;
118 import org.apache.maven.api.spi.ModelTransformer;
119 import org.apache.maven.impl.InternalSession;
120 import org.apache.maven.impl.RequestTraceHelper;
121 import org.apache.maven.impl.util.PhasingExecutor;
122 import org.slf4j.Logger;
123 import org.slf4j.LoggerFactory;
124
125
126
127
128
129
130
131 @Named
132 @Singleton
133 public class DefaultModelBuilder implements ModelBuilder {
134
135 public static final String NAMESPACE_PREFIX = "http://maven.apache.org/POM/";
136 private static final String RAW = "raw";
137 private static final String FILE = "file";
138 private static final String IMPORT = "import";
139 private static final String PARENT = "parent";
140 private static final String MODEL = "model";
141
142 private final Logger logger = LoggerFactory.getLogger(getClass());
143
144 private final ModelProcessor modelProcessor;
145 private final ModelValidator modelValidator;
146 private final ModelNormalizer modelNormalizer;
147 private final ModelInterpolator modelInterpolator;
148 private final ModelPathTranslator modelPathTranslator;
149 private final ModelUrlNormalizer modelUrlNormalizer;
150 private final SuperPomProvider superPomProvider;
151 private final InheritanceAssembler inheritanceAssembler;
152 private final ProfileSelector profileSelector;
153 private final ProfileInjector profileInjector;
154 private final PluginManagementInjector pluginManagementInjector;
155 private final DependencyManagementInjector dependencyManagementInjector;
156 private final DependencyManagementImporter dependencyManagementImporter;
157 private final PluginConfigurationExpander pluginConfigurationExpander;
158 private final ModelVersionParser versionParser;
159 private final List<ModelTransformer> transformers;
160 private final ModelResolver modelResolver;
161 private final Interpolator interpolator;
162 private final PathTranslator pathTranslator;
163 private final RootLocator rootLocator;
164
165 @SuppressWarnings("checkstyle:ParameterNumber")
166 @Inject
167 public DefaultModelBuilder(
168 ModelProcessor modelProcessor,
169 ModelValidator modelValidator,
170 ModelNormalizer modelNormalizer,
171 ModelInterpolator modelInterpolator,
172 ModelPathTranslator modelPathTranslator,
173 ModelUrlNormalizer modelUrlNormalizer,
174 SuperPomProvider superPomProvider,
175 InheritanceAssembler inheritanceAssembler,
176 ProfileSelector profileSelector,
177 ProfileInjector profileInjector,
178 PluginManagementInjector pluginManagementInjector,
179 DependencyManagementInjector dependencyManagementInjector,
180 DependencyManagementImporter dependencyManagementImporter,
181 PluginConfigurationExpander pluginConfigurationExpander,
182 ModelVersionParser versionParser,
183 @Nullable List<ModelTransformer> transformers,
184 ModelResolver modelResolver,
185 Interpolator interpolator,
186 PathTranslator pathTranslator,
187 RootLocator rootLocator) {
188 this.modelProcessor = modelProcessor;
189 this.modelValidator = modelValidator;
190 this.modelNormalizer = modelNormalizer;
191 this.modelInterpolator = modelInterpolator;
192 this.modelPathTranslator = modelPathTranslator;
193 this.modelUrlNormalizer = modelUrlNormalizer;
194 this.superPomProvider = superPomProvider;
195 this.inheritanceAssembler = inheritanceAssembler;
196 this.profileSelector = profileSelector;
197 this.profileInjector = profileInjector;
198 this.pluginManagementInjector = pluginManagementInjector;
199 this.dependencyManagementInjector = dependencyManagementInjector;
200 this.dependencyManagementImporter = dependencyManagementImporter;
201 this.pluginConfigurationExpander = pluginConfigurationExpander;
202 this.versionParser = versionParser;
203 this.transformers = transformers;
204 this.modelResolver = modelResolver;
205 this.interpolator = interpolator;
206 this.pathTranslator = pathTranslator;
207 this.rootLocator = rootLocator;
208 }
209
210 @Override
211 public ModelBuilderSession newSession() {
212 return new ModelBuilderSessionImpl();
213 }
214
215 protected class ModelBuilderSessionImpl implements ModelBuilderSession {
216 ModelBuilderSessionState mainSession;
217
218
219
220
221
222
223
224
225 @Override
226 public ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException {
227 RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(request.getSession(), request);
228 try {
229
230 ModelBuilderSessionState session;
231 if (mainSession == null) {
232 mainSession = new ModelBuilderSessionState(request);
233 session = mainSession;
234 } else {
235 session = mainSession.derive(
236 request,
237 new DefaultModelBuilderResult(request, ProblemCollector.create(mainSession.session)));
238 }
239
240 if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) {
241
242 session.buildBuildPom();
243 } else {
244
245 session.buildEffectiveModel(new LinkedHashSet<>());
246 }
247 return session.result;
248 } finally {
249 RequestTraceHelper.exit(trace);
250 }
251 }
252 }
253
254 protected class ModelBuilderSessionState implements ModelProblemCollector {
255 final Session session;
256 final ModelBuilderRequest request;
257 final DefaultModelBuilderResult result;
258 final Graph dag;
259 final Map<GAKey, Set<ModelSource>> mappedSources;
260
261 String source;
262 Model sourceModel;
263 Model rootModel;
264
265 List<RemoteRepository> pomRepositories;
266 List<RemoteRepository> externalRepositories;
267 List<RemoteRepository> repositories;
268
269
270
271 final Set<String> parentChain;
272
273 ModelBuilderSessionState(ModelBuilderRequest request) {
274 this(
275 request.getSession(),
276 request,
277 new DefaultModelBuilderResult(request, ProblemCollector.create(request.getSession())),
278 new Graph(),
279 new ConcurrentHashMap<>(64),
280 List.of(),
281 repos(request),
282 repos(request),
283 new LinkedHashSet<>());
284 }
285
286 static List<RemoteRepository> repos(ModelBuilderRequest request) {
287 return List.copyOf(
288 request.getRepositories() != null
289 ? request.getRepositories()
290 : request.getSession().getRemoteRepositories());
291 }
292
293 @SuppressWarnings("checkstyle:ParameterNumber")
294 private ModelBuilderSessionState(
295 Session session,
296 ModelBuilderRequest request,
297 DefaultModelBuilderResult result,
298 Graph dag,
299 Map<GAKey, Set<ModelSource>> mappedSources,
300 List<RemoteRepository> pomRepositories,
301 List<RemoteRepository> externalRepositories,
302 List<RemoteRepository> repositories,
303 Set<String> parentChain) {
304 this.session = session;
305 this.request = request;
306 this.result = result;
307 this.dag = dag;
308 this.mappedSources = mappedSources;
309 this.pomRepositories = pomRepositories;
310 this.externalRepositories = externalRepositories;
311 this.repositories = repositories;
312 this.parentChain = parentChain;
313 this.result.setSource(this.request.getSource());
314 }
315
316 ModelBuilderSessionState derive(ModelSource source) {
317 return derive(source, new DefaultModelBuilderResult(request, ProblemCollector.create(session)));
318 }
319
320 ModelBuilderSessionState derive(ModelSource source, DefaultModelBuilderResult result) {
321 return derive(ModelBuilderRequest.build(request, source), result);
322 }
323
324
325
326
327 ModelBuilderSessionState derive(ModelBuilderRequest request) {
328 return derive(request, new DefaultModelBuilderResult(request, ProblemCollector.create(session)));
329 }
330
331 ModelBuilderSessionState derive(ModelBuilderRequest request, DefaultModelBuilderResult result) {
332 if (session != request.getSession()) {
333 throw new IllegalArgumentException("Session mismatch");
334 }
335
336
337 return new ModelBuilderSessionState(
338 session,
339 request,
340 result,
341 dag,
342 mappedSources,
343 pomRepositories,
344 externalRepositories,
345 repositories,
346 new LinkedHashSet<>());
347 }
348
349 @Override
350 public String toString() {
351 return "ModelBuilderSession[" + "session="
352 + session + ", " + "request="
353 + request + ", " + "result="
354 + result + ']';
355 }
356
357 PhasingExecutor createExecutor() {
358 return new PhasingExecutor(Executors.newFixedThreadPool(getParallelism()));
359 }
360
361 private int getParallelism() {
362 int parallelism = Runtime.getRuntime().availableProcessors() / 2 + 1;
363 try {
364 String str = request.getUserProperties().get(Constants.MAVEN_MODEL_BUILDER_PARALLELISM);
365 if (str != null) {
366 parallelism = Integer.parseInt(str);
367 }
368 } catch (Exception e) {
369
370 }
371 return Math.max(1, Math.min(parallelism, Runtime.getRuntime().availableProcessors()));
372 }
373
374 public Model getRawModel(Path from, String groupId, String artifactId) {
375 ModelSource source = getSource(groupId, artifactId);
376 if (source != null) {
377 if (addEdge(from, source.getPath())) {
378 return null;
379 }
380 try {
381 return derive(source).readRawModel();
382 } catch (ModelBuilderException e) {
383
384 }
385 }
386 return null;
387 }
388
389 public Model getRawModel(Path from, Path path) {
390 if (!Files.isRegularFile(path)) {
391 throw new IllegalArgumentException("Not a regular file: " + path);
392 }
393 if (addEdge(from, path)) {
394 return null;
395 }
396 try {
397 return derive(Sources.buildSource(path)).readRawModel();
398 } catch (ModelBuilderException e) {
399
400 }
401 return null;
402 }
403
404
405
406
407 private boolean addEdge(Path from, Path p) {
408 try {
409 dag.addEdge(from.toString(), p.toString());
410 return false;
411 } catch (Graph.CycleDetectedException e) {
412 add(Severity.FATAL, Version.BASE, "Cycle detected between models at " + from + " and " + p, null, e);
413 return true;
414 }
415 }
416
417 public ModelSource getSource(String groupId, String artifactId) {
418 Set<ModelSource> sources = mappedSources.get(new GAKey(groupId, artifactId));
419 if (sources != null) {
420 return sources.stream()
421 .reduce((a, b) -> {
422 throw new IllegalStateException(String.format(
423 "No unique Source for %s:%s: %s and %s",
424 groupId, artifactId, a.getLocation(), b.getLocation()));
425 })
426 .orElse(null);
427 }
428 return null;
429 }
430
431 public void putSource(String groupId, String artifactId, ModelSource source) {
432 mappedSources
433 .computeIfAbsent(new GAKey(groupId, artifactId), k -> new HashSet<>())
434 .add(source);
435
436 if (groupId != null) {
437 putSource(null, artifactId, source);
438 }
439 }
440
441 @Override
442 public ProblemCollector<ModelProblem> getProblemCollector() {
443 return result.getProblemCollector();
444 }
445
446 @Override
447 public void setSource(String source) {
448 this.source = source;
449 this.sourceModel = null;
450 }
451
452 @Override
453 public void setSource(Model source) {
454 this.sourceModel = source;
455 this.source = null;
456
457 if (rootModel == null) {
458 rootModel = source;
459 }
460 }
461
462 @Override
463 public String getSource() {
464 if (source == null && sourceModel != null) {
465 source = ModelProblemUtils.toPath(sourceModel);
466 }
467 return source;
468 }
469
470 private String getModelId() {
471 return ModelProblemUtils.toId(sourceModel);
472 }
473
474 @Override
475 public void setRootModel(Model rootModel) {
476 this.rootModel = rootModel;
477 }
478
479 @Override
480 public Model getRootModel() {
481 return rootModel;
482 }
483
484 @Override
485 public void add(
486 BuilderProblem.Severity severity,
487 ModelProblem.Version version,
488 String message,
489 InputLocation location,
490 Exception exception) {
491 int line = -1;
492 int column = -1;
493 String source = null;
494 String modelId = null;
495
496 if (location != null) {
497 line = location.getLineNumber();
498 column = location.getColumnNumber();
499 if (location.getSource() != null) {
500 modelId = location.getSource().getModelId();
501 source = location.getSource().getLocation();
502 }
503 }
504
505 if (modelId == null) {
506 modelId = getModelId();
507 source = getSource();
508 }
509
510 if (line <= 0 && column <= 0 && exception instanceof ModelParserException e) {
511 line = e.getLineNumber();
512 column = e.getColumnNumber();
513 }
514
515 ModelProblem problem =
516 new DefaultModelProblem(message, severity, version, source, line, column, modelId, exception);
517
518 add(problem);
519 }
520
521 @Override
522 public ModelBuilderException newModelBuilderException() {
523 return new ModelBuilderException(result);
524 }
525
526 public void mergeRepositories(Model model, boolean replace) {
527 if (model.getRepositories().isEmpty()
528 || InternalSession.from(session).getSession().isIgnoreArtifactDescriptorRepositories()) {
529 return;
530 }
531
532 Model interpolatedModel = interpolateModel(
533 Model.newBuilder()
534 .pomFile(model.getPomFile())
535 .properties(model.getProperties())
536 .repositories(model.getRepositories())
537 .build(),
538 request,
539 this);
540 List<RemoteRepository> repos = interpolatedModel.getRepositories().stream()
541
542
543
544 .filter(repo -> repo.getUrl() != null && !repo.getUrl().contains("${"))
545 .map(session::createRemoteRepository)
546 .toList();
547 if (replace) {
548 Set<String> ids = repos.stream().map(RemoteRepository::getId).collect(Collectors.toSet());
549 repositories = repositories.stream()
550 .filter(r -> !ids.contains(r.getId()))
551 .toList();
552 pomRepositories = pomRepositories.stream()
553 .filter(r -> !ids.contains(r.getId()))
554 .toList();
555 } else {
556 Set<String> ids =
557 pomRepositories.stream().map(RemoteRepository::getId).collect(Collectors.toSet());
558 repos = repos.stream().filter(r -> !ids.contains(r.getId())).toList();
559 }
560
561 RepositoryFactory repositoryFactory = session.getService(RepositoryFactory.class);
562 if (request.getRepositoryMerging() == ModelBuilderRequest.RepositoryMerging.REQUEST_DOMINANT) {
563 repositories = repositoryFactory.aggregate(session, repositories, repos, true);
564 pomRepositories = repositories;
565 } else {
566 pomRepositories = repositoryFactory.aggregate(session, pomRepositories, repos, true);
567 repositories = repositoryFactory.aggregate(session, pomRepositories, externalRepositories, false);
568 }
569 }
570
571
572
573
574
575 Model transformFileToRaw(Model model) {
576 if (model.getDependencies().isEmpty()) {
577 return model;
578 }
579 List<Dependency> newDeps = new ArrayList<>(model.getDependencies().size());
580 boolean changed = false;
581 for (Dependency dep : model.getDependencies()) {
582 Dependency newDep = null;
583 if (dep.getVersion() == null) {
584 newDep = inferDependencyVersion(model, dep);
585 if (newDep != null) {
586 changed = true;
587 }
588 } else if (dep.getGroupId() == null) {
589
590 newDep = inferDependencyGroupId(model, dep);
591 if (newDep != null) {
592 changed = true;
593 }
594 }
595 newDeps.add(newDep == null ? dep : newDep);
596 }
597 return changed ? model.withDependencies(newDeps) : model;
598 }
599
600 private Dependency inferDependencyVersion(Model model, Dependency dep) {
601 Model depModel = getRawModel(model.getPomFile(), dep.getGroupId(), dep.getArtifactId());
602 if (depModel == null) {
603 return null;
604 }
605 Dependency.Builder depBuilder = Dependency.newBuilder(dep);
606 String version = depModel.getVersion();
607 InputLocation versionLocation = depModel.getLocation("version");
608 if (version == null && depModel.getParent() != null) {
609 version = depModel.getParent().getVersion();
610 versionLocation = depModel.getParent().getLocation("version");
611 }
612 depBuilder.version(version).location("version", versionLocation);
613 if (dep.getGroupId() == null) {
614 String depGroupId = depModel.getGroupId();
615 InputLocation groupIdLocation = depModel.getLocation("groupId");
616 if (depGroupId == null && depModel.getParent() != null) {
617 depGroupId = depModel.getParent().getGroupId();
618 groupIdLocation = depModel.getParent().getLocation("groupId");
619 }
620 depBuilder.groupId(depGroupId).location("groupId", groupIdLocation);
621 }
622 return depBuilder.build();
623 }
624
625 private Dependency inferDependencyGroupId(Model model, Dependency dep) {
626 Model depModel = getRawModel(model.getPomFile(), dep.getGroupId(), dep.getArtifactId());
627 if (depModel == null) {
628 return null;
629 }
630 Dependency.Builder depBuilder = Dependency.newBuilder(dep);
631 String depGroupId = depModel.getGroupId();
632 InputLocation groupIdLocation = depModel.getLocation("groupId");
633 if (depGroupId == null && depModel.getParent() != null) {
634 depGroupId = depModel.getParent().getGroupId();
635 groupIdLocation = depModel.getParent().getLocation("groupId");
636 }
637 depBuilder.groupId(depGroupId).location("groupId", groupIdLocation);
638 return depBuilder.build();
639 }
640
641 String replaceCiFriendlyVersion(Map<String, String> properties, String version) {
642 return version != null ? interpolator.interpolate(version, properties::get) : null;
643 }
644
645
646
647
648
649
650
651 private Map<String, String> getEnhancedProperties(Model model, Path rootDirectory) {
652 Map<String, String> properties = new HashMap<>();
653
654
655 if (model.getProjectDirectory() != null) {
656 String basedir = model.getProjectDirectory().toString();
657 String basedirUri = model.getProjectDirectory().toUri().toString();
658 properties.put("basedir", basedir);
659 properties.put("project.basedir", basedir);
660 properties.put("project.basedir.uri", basedirUri);
661 }
662 try {
663 String root = rootDirectory.toString();
664 String rootUri = rootDirectory.toUri().toString();
665 properties.put("project.rootDirectory", root);
666 properties.put("project.rootDirectory.uri", rootUri);
667 } catch (IllegalStateException e) {
668
669 }
670
671
672 if (!Objects.equals(rootDirectory, model.getProjectDirectory())) {
673 Path rootModelPath = modelProcessor.locateExistingPom(rootDirectory);
674 if (rootModelPath != null) {
675
676
677
678 if (isParentWithinRootDirectory(rootModelPath, rootDirectory)) {
679 Model rootModel =
680 derive(Sources.buildSource(rootModelPath)).readFileModel();
681 properties.putAll(getPropertiesWithProfiles(rootModel, properties));
682 }
683 }
684 } else {
685 properties.putAll(getPropertiesWithProfiles(model, properties));
686 }
687
688 return properties;
689 }
690
691
692
693
694
695
696
697
698 private Map<String, String> getPropertiesWithProfiles(Model model, Map<String, String> baseProperties) {
699 Map<String, String> properties = new HashMap<>();
700
701
702 properties.putAll(baseProperties);
703
704
705 properties.putAll(model.getProperties());
706
707 try {
708
709 DefaultProfileActivationContext profileContext = getProfileActivationContext(request, model);
710
711
712 List<Profile> activeProfiles = getActiveProfiles(model.getProfiles(), profileContext);
713
714 for (Profile profile : activeProfiles) {
715 properties.putAll(profile.getProperties());
716 }
717 } catch (Exception e) {
718
719
720 logger.warn("Failed to activate profiles for CI-friendly version processing: {}", e.getMessage());
721 logger.debug("Profile activation failure details", e);
722 }
723
724
725 properties.putAll(session.getEffectiveProperties());
726
727 return properties;
728 }
729
730
731
732
733
734 private Map<String, String> getPropertiesWithProfiles(Model model) {
735 return getPropertiesWithProfiles(model, new HashMap<>());
736 }
737
738 private void buildBuildPom() throws ModelBuilderException {
739
740 Path top = request.getSource().getPath();
741 if (top == null) {
742 throw new IllegalStateException("Recursive build requested but source has no path");
743 }
744 top = top.toAbsolutePath().normalize();
745
746
747 Path rootDirectory;
748 try {
749 rootDirectory = session.getRootDirectory();
750 } catch (IllegalStateException e) {
751 rootDirectory = session.getService(RootLocator.class).findMandatoryRoot(top);
752 }
753
754
755 Path root = modelProcessor.locateExistingPom(rootDirectory);
756 if (root != null) {
757 root = root.toAbsolutePath().normalize();
758 } else {
759 root = top;
760 }
761
762
763 loadFromRoot(root, top);
764
765
766 if (hasErrors()) {
767 throw newModelBuilderException();
768 }
769
770
771
772 var allResults = results(result).toList();
773 List<RuntimeException> exceptions = new CopyOnWriteArrayList<>();
774 InternalSession session = InternalSession.from(this.session);
775 RequestTrace trace = session.getCurrentTrace();
776 try (PhasingExecutor executor = createExecutor()) {
777 for (DefaultModelBuilderResult r : allResults) {
778 executor.execute(() -> {
779 ModelBuilderSessionState mbs = derive(r.getSource(), r);
780 session.setCurrentTrace(trace);
781 try {
782 mbs.buildEffectiveModel(new LinkedHashSet<>());
783 } catch (ModelBuilderException e) {
784
785
786 for (var problem : e.getResult()
787 .getProblemCollector()
788 .problems()
789 .toList()) {
790 getProblemCollector().reportProblem(problem);
791 }
792 } catch (RuntimeException t) {
793 exceptions.add(t);
794 } finally {
795 session.setCurrentTrace(null);
796 }
797 });
798 }
799 }
800
801
802 if (exceptions.size() == 1) {
803 throw exceptions.get(0);
804 } else if (exceptions.size() > 1) {
805 MavenException fatalException = new MavenException("Multiple fatal exceptions occurred");
806 exceptions.forEach(fatalException::addSuppressed);
807 throw fatalException;
808 } else if (hasErrors()) {
809 throw newModelBuilderException();
810 }
811 }
812
813
814
815
816
817
818
819
820
821 Stream<DefaultModelBuilderResult> results(DefaultModelBuilderResult r) {
822 return Stream.concat(Stream.of(r), r.getChildren().stream().flatMap(this::results));
823 }
824
825 private void loadFromRoot(Path root, Path top) {
826 try (PhasingExecutor executor = createExecutor()) {
827 DefaultModelBuilderResult r = Objects.equals(top, root)
828 ? result
829 : new DefaultModelBuilderResult(request, ProblemCollector.create(session));
830 loadFilePom(executor, top, root, Set.of(), r);
831 }
832 if (result.getFileModel() == null && !Objects.equals(top, root)) {
833 logger.warn(
834 "The top project ({}) cannot be found in the reactor from root project ({}). "
835 + "Make sure the root directory is correct (a missing '.mvn' directory in the root "
836 + "project is the most common cause) and the project is correctly included "
837 + "in the reactor (missing activated profiles, command line options, etc.). For this "
838 + "build, the top project will be used as the root project.",
839 top,
840 root);
841 mappedSources.clear();
842 loadFromRoot(top, top);
843 }
844 }
845
846 private void loadFilePom(
847 Executor executor, Path top, Path pom, Set<Path> parents, DefaultModelBuilderResult r) {
848 try {
849 Path pomDirectory = Files.isDirectory(pom) ? pom : pom.getParent();
850 ModelSource src = Sources.buildSource(pom);
851 Model model = derive(src, r).readFileModel();
852
853
854 putSource(getGroupId(model), model.getArtifactId(), src);
855 Model activated = activateFileModel(model);
856 for (String subproject : getSubprojects(activated)) {
857 if (subproject == null || subproject.isEmpty()) {
858 continue;
859 }
860
861 subproject = subproject.replace('\\', File.separatorChar).replace('/', File.separatorChar);
862
863 Path rawSubprojectFile = modelProcessor.locateExistingPom(pomDirectory.resolve(subproject));
864
865 if (rawSubprojectFile == null) {
866 ModelProblem problem = new DefaultModelProblem(
867 "Child subproject " + subproject + " of " + pomDirectory + " does not exist",
868 Severity.ERROR,
869 Version.BASE,
870 model,
871 -1,
872 -1,
873 null);
874 r.getProblemCollector().reportProblem(problem);
875 continue;
876 }
877
878 Path subprojectFile = rawSubprojectFile.toAbsolutePath().normalize();
879
880 if (parents.contains(subprojectFile)) {
881 StringBuilder buffer = new StringBuilder(256);
882 for (Path aggregatorFile : parents) {
883 buffer.append(aggregatorFile).append(" -> ");
884 }
885 buffer.append(subprojectFile);
886
887 ModelProblem problem = new DefaultModelProblem(
888 "Child subproject " + subprojectFile + " of " + pom + " forms aggregation cycle "
889 + buffer,
890 Severity.ERROR,
891 Version.BASE,
892 model,
893 -1,
894 -1,
895 null);
896 r.getProblemCollector().reportProblem(problem);
897 continue;
898 }
899
900 DefaultModelBuilderResult cr = Objects.equals(top, subprojectFile)
901 ? result
902 : new DefaultModelBuilderResult(request, ProblemCollector.create(session));
903 if (request.isRecursive()) {
904 r.getChildren().add(cr);
905 }
906
907 InternalSession session = InternalSession.from(this.session);
908 RequestTrace trace = session.getCurrentTrace();
909 executor.execute(() -> {
910 session.setCurrentTrace(trace);
911 try {
912 loadFilePom(executor, top, subprojectFile, concat(parents, pom), cr);
913 } finally {
914 session.setCurrentTrace(null);
915 }
916 });
917 }
918 } catch (ModelBuilderException e) {
919
920 add(Severity.ERROR, Version.V40, "Failed to load project " + pom, e);
921 }
922 }
923
924 static <T> Set<T> concat(Set<T> a, T b) {
925 Set<T> result = new HashSet<>(a);
926 result.add(b);
927 return Set.copyOf(result);
928 }
929
930 void buildEffectiveModel(Collection<String> importIds) throws ModelBuilderException {
931 Model resultModel = readEffectiveModel();
932 setSource(resultModel);
933 setRootModel(resultModel);
934
935
936 resultModel =
937 modelPathTranslator.alignToBaseDirectory(resultModel, resultModel.getProjectDirectory(), request);
938
939
940 resultModel = pluginManagementInjector.injectManagement(resultModel, request, this);
941
942
943 if (request.getRequestType() != ModelBuilderRequest.RequestType.CONSUMER_DEPENDENCY) {
944 org.apache.maven.api.services.ModelTransformer lifecycleBindingsInjector =
945 request.getLifecycleBindingsInjector();
946 if (lifecycleBindingsInjector != null) {
947 resultModel = lifecycleBindingsInjector.transform(resultModel, request, this);
948 }
949 }
950
951
952 resultModel = importDependencyManagement(resultModel, importIds);
953
954
955 resultModel = dependencyManagementInjector.injectManagement(resultModel, request, this);
956
957 resultModel = modelNormalizer.injectDefaultValues(resultModel, request, this);
958
959 if (request.getRequestType() != ModelBuilderRequest.RequestType.CONSUMER_DEPENDENCY) {
960
961 resultModel = pluginConfigurationExpander.expandPluginConfiguration(resultModel, request, this);
962 }
963
964 for (var transformer : transformers) {
965 resultModel = transformer.transformEffectiveModel(resultModel);
966 }
967
968 result.setEffectiveModel(resultModel);
969
970 if (result.getFileModel().getParent() != null
971 && result.getFileModel().getParent().getRelativePath() == null) {
972 result.setFileModel(result.getFileModel()
973 .withParent(result.getFileModel()
974 .getParent()
975 .withRelativePath(resultModel.getParent().getRelativePath())));
976 }
977
978
979 modelValidator.validateEffectiveModel(
980 session,
981 resultModel,
982 isBuildRequest() ? ModelValidator.VALIDATION_LEVEL_STRICT : ModelValidator.VALIDATION_LEVEL_MINIMAL,
983 this);
984
985 if (hasErrors()) {
986 throw newModelBuilderException();
987 }
988 }
989
990 Model readParent(
991 Model childModel,
992 Parent parent,
993 DefaultProfileActivationContext profileActivationContext,
994 Set<String> parentChain) {
995 Model parentModel;
996
997 if (parent != null) {
998
999 String parentId = parent.getGroupId() + ":" + parent.getArtifactId() + ":" + parent.getVersion();
1000 if (!parentChain.add(parentId)) {
1001 StringBuilder message = new StringBuilder("The parents form a cycle: ");
1002 for (String id : parentChain) {
1003 message.append(id).append(" -> ");
1004 }
1005 message.append(parentId);
1006
1007 add(Severity.FATAL, Version.BASE, message.toString());
1008 throw newModelBuilderException();
1009 }
1010
1011 try {
1012 parentModel = resolveParent(childModel, parent, profileActivationContext, parentChain);
1013
1014 if (!"pom".equals(parentModel.getPackaging())) {
1015 add(
1016 Severity.ERROR,
1017 Version.BASE,
1018 "Invalid packaging for parent POM " + ModelProblemUtils.toSourceHint(parentModel)
1019 + ", must be \"pom\" but is \"" + parentModel.getPackaging() + "\"",
1020 parentModel.getLocation("packaging"));
1021 }
1022 result.setParentModel(parentModel);
1023
1024
1025 if (parentModel.getParent() != null) {
1026 readParent(parentModel, parentModel.getParent(), profileActivationContext, parentChain);
1027 }
1028 } finally {
1029
1030 parentChain.remove(parentId);
1031 }
1032 } else {
1033 String superModelVersion = childModel.getModelVersion();
1034 if (superModelVersion == null || !KNOWN_MODEL_VERSIONS.contains(superModelVersion)) {
1035
1036
1037
1038 superModelVersion = MODEL_VERSION_4_0_0;
1039 }
1040 parentModel = getSuperModel(superModelVersion);
1041 }
1042
1043 return parentModel;
1044 }
1045
1046 private Model resolveParent(
1047 Model childModel,
1048 Parent parent,
1049 DefaultProfileActivationContext profileActivationContext,
1050 Set<String> parentChain)
1051 throws ModelBuilderException {
1052 Model parentModel = null;
1053 if (isBuildRequest()) {
1054 parentModel = readParentLocally(childModel, parent, profileActivationContext, parentChain);
1055 }
1056 if (parentModel == null) {
1057 parentModel = resolveAndReadParentExternally(childModel, parent, profileActivationContext, parentChain);
1058 }
1059 return parentModel;
1060 }
1061
1062 private Model readParentLocally(
1063 Model childModel,
1064 Parent parent,
1065 DefaultProfileActivationContext profileActivationContext,
1066 Set<String> parentChain)
1067 throws ModelBuilderException {
1068 ModelSource candidateSource;
1069 String parentPath = parent.getRelativePath();
1070 if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) {
1071 if (parentPath != null && !parentPath.isEmpty()) {
1072 candidateSource = request.getSource().resolve(modelProcessor::locateExistingPom, parentPath);
1073 if (candidateSource == null) {
1074 wrongParentRelativePath(childModel);
1075 return null;
1076 }
1077 } else {
1078 candidateSource =
1079 resolveReactorModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
1080 if (candidateSource == null && parentPath == null) {
1081 candidateSource = request.getSource().resolve(modelProcessor::locateExistingPom, "..");
1082 }
1083 }
1084 } else {
1085 candidateSource = resolveReactorModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
1086 if (candidateSource == null) {
1087 if (parentPath == null) {
1088 parentPath = "..";
1089 }
1090 if (!parentPath.isEmpty()) {
1091 candidateSource = request.getSource().resolve(modelProcessor::locateExistingPom, parentPath);
1092 }
1093 }
1094 }
1095
1096 if (candidateSource == null) {
1097 return null;
1098 }
1099
1100
1101
1102 String sourceLocation = candidateSource.getLocation();
1103
1104 if (!parentChain.add(sourceLocation)) {
1105 StringBuilder message = new StringBuilder("The parents form a cycle: ");
1106 for (String location : parentChain) {
1107 message.append(location).append(" -> ");
1108 }
1109 message.append(sourceLocation);
1110
1111 add(Severity.FATAL, Version.BASE, message.toString());
1112 throw newModelBuilderException();
1113 }
1114
1115 try {
1116 ModelBuilderSessionState derived = derive(candidateSource);
1117 Model candidateModel = derived.readAsParentModel(profileActivationContext, parentChain);
1118 addActivePomProfiles(derived.result.getActivePomProfiles());
1119
1120 String groupId = getGroupId(candidateModel);
1121 String artifactId = candidateModel.getArtifactId();
1122 String version = getVersion(candidateModel);
1123
1124
1125 if (groupId == null
1126 || !groupId.equals(parent.getGroupId())
1127 || artifactId == null
1128 || !artifactId.equals(parent.getArtifactId())) {
1129 mismatchRelativePathAndGA(childModel, groupId, artifactId);
1130 return null;
1131 }
1132
1133 if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) {
1134 try {
1135 VersionRange parentRange = versionParser.parseVersionRange(parent.getVersion());
1136 if (!parentRange.contains(versionParser.parseVersion(version))) {
1137
1138 return null;
1139 }
1140
1141
1142
1143 String rawChildModelVersion = childModel.getVersion();
1144
1145 if (rawChildModelVersion == null) {
1146
1147 add(Severity.FATAL, Version.V31, "Version must be a constant", childModel.getLocation(""));
1148
1149 } else {
1150 if (rawChildVersionReferencesParent(rawChildModelVersion)) {
1151
1152 add(
1153 Severity.FATAL,
1154 Version.V31,
1155 "Version must be a constant",
1156 childModel.getLocation("version"));
1157 }
1158 }
1159
1160
1161 } catch (VersionParserException e) {
1162
1163 return null;
1164 }
1165 }
1166 return candidateModel;
1167 } finally {
1168
1169 parentChain.remove(sourceLocation);
1170 }
1171 }
1172
1173 private void mismatchRelativePathAndGA(Model childModel, String groupId, String artifactId) {
1174 Parent parent = childModel.getParent();
1175 StringBuilder buffer = new StringBuilder(256);
1176 buffer.append("'parent.relativePath'");
1177 if (childModel != getRootModel()) {
1178 buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel));
1179 }
1180 buffer.append(" points at ").append(groupId).append(':').append(artifactId);
1181 buffer.append(" instead of ").append(parent.getGroupId()).append(':');
1182 buffer.append(parent.getArtifactId()).append(", please verify your project structure");
1183
1184 setSource(childModel);
1185 boolean warn = MODEL_VERSION_4_0_0.equals(childModel.getModelVersion())
1186 || childModel.getParent().getRelativePath() == null;
1187 add(warn ? Severity.WARNING : Severity.FATAL, Version.BASE, buffer.toString(), parent.getLocation(""));
1188 }
1189
1190 private void wrongParentRelativePath(Model childModel) {
1191 Parent parent = childModel.getParent();
1192 String parentPath = parent.getRelativePath();
1193 StringBuilder buffer = new StringBuilder(256);
1194 buffer.append("'parent.relativePath'");
1195 if (childModel != getRootModel()) {
1196 buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel));
1197 }
1198 buffer.append(" points at '").append(parentPath);
1199 buffer.append("' but no POM could be found, please verify your project structure");
1200
1201 setSource(childModel);
1202 add(Severity.FATAL, Version.BASE, buffer.toString(), parent.getLocation(""));
1203 }
1204
1205 Model resolveAndReadParentExternally(
1206 Model childModel,
1207 Parent parent,
1208 DefaultProfileActivationContext profileActivationContext,
1209 Set<String> parentChain)
1210 throws ModelBuilderException {
1211 ModelBuilderRequest request = this.request;
1212 setSource(childModel);
1213
1214 String groupId = parent.getGroupId();
1215 String artifactId = parent.getArtifactId();
1216 String version = parent.getVersion();
1217
1218
1219 if (!childModel.getRepositories().isEmpty()) {
1220 var previousRepositories = repositories;
1221 mergeRepositories(childModel, false);
1222 if (!Objects.equals(previousRepositories, repositories)) {
1223 if (logger.isDebugEnabled()) {
1224 logger.debug("Merging repositories from " + childModel.getId() + "\n"
1225 + repositories.stream()
1226 .map(Object::toString)
1227 .collect(Collectors.joining("\n", " ", "")));
1228 }
1229 }
1230 }
1231
1232 ModelSource modelSource;
1233 try {
1234 modelSource = resolveReactorModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
1235 if (modelSource == null) {
1236 AtomicReference<Parent> modified = new AtomicReference<>();
1237 modelSource = modelResolver.resolveModel(request.getSession(), repositories, parent, modified);
1238 if (modified.get() != null) {
1239 parent = modified.get();
1240 }
1241 }
1242 } catch (ModelResolverException e) {
1243
1244 StringBuilder buffer = new StringBuilder(256);
1245 buffer.append("Non-resolvable parent POM");
1246 if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
1247 buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
1248 }
1249 if (childModel != getRootModel()) {
1250 buffer.append(" for ").append(ModelProblemUtils.toId(childModel));
1251 }
1252 buffer.append(": ").append(e.getMessage());
1253 if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) {
1254 buffer.append(" and parent could not be found in reactor");
1255 }
1256
1257 add(Severity.FATAL, Version.BASE, buffer.toString(), parent.getLocation(""), e);
1258 throw newModelBuilderException();
1259 }
1260
1261 ModelBuilderRequest lenientRequest = ModelBuilderRequest.builder(request)
1262 .requestType(ModelBuilderRequest.RequestType.CONSUMER_PARENT)
1263 .source(modelSource)
1264 .build();
1265
1266 Model parentModel = derive(lenientRequest).readAsParentModel(profileActivationContext, parentChain);
1267
1268 if (!parent.getVersion().equals(version)) {
1269 String rawChildModelVersion = childModel.getVersion();
1270
1271 if (rawChildModelVersion == null) {
1272
1273 add(Severity.FATAL, Version.V31, "Version must be a constant", childModel.getLocation(""));
1274 } else {
1275 if (rawChildVersionReferencesParent(rawChildModelVersion)) {
1276
1277 add(
1278 Severity.FATAL,
1279 Version.V31,
1280 "Version must be a constant",
1281 childModel.getLocation("version"));
1282 }
1283 }
1284
1285
1286 }
1287
1288 return parentModel;
1289 }
1290
1291 Model activateFileModel(Model inputModel) throws ModelBuilderException {
1292 setRootModel(inputModel);
1293
1294
1295 DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel);
1296
1297 setSource("(external profiles)");
1298 List<Profile> activeExternalProfiles = getActiveProfiles(request.getProfiles(), profileActivationContext);
1299
1300 result.setActiveExternalProfiles(activeExternalProfiles);
1301
1302 if (!activeExternalProfiles.isEmpty()) {
1303 Map<String, String> profileProps = new HashMap<>();
1304 for (Profile profile : activeExternalProfiles) {
1305 profileProps.putAll(profile.getProperties());
1306 }
1307 profileProps.putAll(request.getUserProperties());
1308 profileActivationContext.setUserProperties(profileProps);
1309 }
1310
1311 profileActivationContext.setModel(inputModel);
1312 setSource(inputModel);
1313 List<Profile> activePomProfiles = getActiveProfiles(inputModel.getProfiles(), profileActivationContext);
1314
1315
1316 setSource(inputModel);
1317 inputModel = modelNormalizer.mergeDuplicates(inputModel, request, this);
1318
1319 Map<String, Activation> interpolatedActivations = getProfileActivations(inputModel);
1320 inputModel = injectProfileActivations(inputModel, interpolatedActivations);
1321
1322
1323 inputModel = profileInjector.injectProfiles(inputModel, activePomProfiles, request, this);
1324 inputModel = profileInjector.injectProfiles(inputModel, activeExternalProfiles, request, this);
1325
1326 return inputModel;
1327 }
1328
1329 @SuppressWarnings("checkstyle:methodlength")
1330 private Model readEffectiveModel() throws ModelBuilderException {
1331 Model inputModel = readRawModel();
1332 if (hasFatalErrors()) {
1333 throw newModelBuilderException();
1334 }
1335
1336 setRootModel(inputModel);
1337
1338 Model activatedFileModel = activateFileModel(inputModel);
1339
1340
1341 DefaultProfileActivationContext profileActivationContext =
1342 getProfileActivationContext(request, activatedFileModel);
1343
1344 List<Profile> activeExternalProfiles = result.getActiveExternalProfiles();
1345
1346 if (!activeExternalProfiles.isEmpty()) {
1347 Map<String, String> profileProps = new HashMap<>();
1348 for (Profile profile : activeExternalProfiles) {
1349 profileProps.putAll(profile.getProperties());
1350 }
1351 profileProps.putAll(request.getUserProperties());
1352 profileActivationContext.setUserProperties(profileProps);
1353 }
1354
1355 Model parentModel = readParent(
1356 activatedFileModel, activatedFileModel.getParent(), profileActivationContext, parentChain);
1357
1358
1359
1360 if (inputModel.getParent() != null && inputModel.getParent().getRelativePath() == null) {
1361 String relPath;
1362 if (parentModel.getPomFile() != null && isBuildRequest()) {
1363 relPath = inputModel
1364 .getPomFile()
1365 .getParent()
1366 .toAbsolutePath()
1367 .relativize(
1368 parentModel.getPomFile().toAbsolutePath().getParent())
1369 .toString();
1370 } else {
1371 relPath = "..";
1372 }
1373 inputModel = inputModel.withParent(inputModel.getParent().withRelativePath(relPath));
1374 }
1375
1376 Model model = inheritanceAssembler.assembleModelInheritance(inputModel, parentModel, request, this);
1377
1378
1379 profileActivationContext.setModel(model);
1380
1381
1382 List<Profile> activePomProfiles = getActiveProfiles(model.getProfiles(), profileActivationContext);
1383 model = profileInjector.injectProfiles(model, activePomProfiles, request, this);
1384 model = profileInjector.injectProfiles(model, activeExternalProfiles, request, this);
1385
1386 addActivePomProfiles(activePomProfiles);
1387
1388
1389 Model resultModel = model;
1390 resultModel = interpolateModel(resultModel, request, this);
1391
1392
1393 resultModel = modelNormalizer.mergeDuplicates(resultModel, request, this);
1394
1395
1396 resultModel = modelUrlNormalizer.normalize(resultModel, request);
1397
1398
1399 if (!resultModel.getRepositories().isEmpty()) {
1400 List<String> oldRepos =
1401 repositories.stream().map(Object::toString).toList();
1402 mergeRepositories(resultModel, true);
1403 List<String> newRepos =
1404 repositories.stream().map(Object::toString).toList();
1405 if (!Objects.equals(oldRepos, newRepos)) {
1406 logger.debug("Replacing repositories from " + resultModel.getId() + "\n"
1407 + newRepos.stream().map(s -> " " + s).collect(Collectors.joining("\n")));
1408 }
1409 }
1410
1411 return resultModel;
1412 }
1413
1414 private void addActivePomProfiles(List<Profile> activePomProfiles) {
1415 if (activePomProfiles != null) {
1416 if (result.getActivePomProfiles() == null) {
1417 result.setActivePomProfiles(new ArrayList<>());
1418 }
1419 result.getActivePomProfiles().addAll(activePomProfiles);
1420 }
1421 }
1422
1423 private List<Profile> getActiveProfiles(
1424 Collection<Profile> interpolatedProfiles, DefaultProfileActivationContext profileActivationContext) {
1425 if (isBuildRequestWithActivation()) {
1426 return profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, this);
1427 } else {
1428 return List.of();
1429 }
1430 }
1431
1432 Model readFileModel() throws ModelBuilderException {
1433 Model model = cache(request.getSource(), FILE, this::doReadFileModel);
1434
1435 result.setFileModel(model);
1436 return model;
1437 }
1438
1439 @SuppressWarnings("checkstyle:methodlength")
1440 Model doReadFileModel() throws ModelBuilderException {
1441 ModelSource modelSource = request.getSource();
1442 Model model;
1443 Path rootDirectory;
1444 setSource(modelSource.getLocation());
1445 logger.debug("Reading file model from " + modelSource.getLocation());
1446 try {
1447 boolean strict = isBuildRequest();
1448 try {
1449 rootDirectory = request.getSession().getRootDirectory();
1450 } catch (IllegalStateException ignore) {
1451 rootDirectory = modelSource.getPath();
1452 while (rootDirectory != null && !Files.isDirectory(rootDirectory)) {
1453 rootDirectory = rootDirectory.getParent();
1454 }
1455 }
1456 try (InputStream is = modelSource.openStream()) {
1457 model = modelProcessor.read(XmlReaderRequest.builder()
1458 .strict(strict)
1459 .location(modelSource.getLocation())
1460 .path(modelSource.getPath())
1461 .rootDirectory(rootDirectory)
1462 .inputStream(is)
1463 .transformer(new InterningTransformer(session))
1464 .build());
1465 } catch (XmlReaderException e) {
1466 if (!strict) {
1467 throw e;
1468 }
1469 try (InputStream is = modelSource.openStream()) {
1470 model = modelProcessor.read(XmlReaderRequest.builder()
1471 .strict(false)
1472 .location(modelSource.getLocation())
1473 .path(modelSource.getPath())
1474 .rootDirectory(rootDirectory)
1475 .inputStream(is)
1476 .transformer(new InterningTransformer(session))
1477 .build());
1478 } catch (XmlReaderException ne) {
1479
1480 throw e;
1481 }
1482
1483 add(
1484 Severity.ERROR,
1485 Version.V20,
1486 "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage(),
1487 e);
1488 }
1489
1490 InputLocation loc = model.getLocation("");
1491 InputSource v4src = loc != null ? loc.getSource() : null;
1492 if (v4src != null) {
1493 try {
1494 Field field = InputSource.class.getDeclaredField("modelId");
1495 field.setAccessible(true);
1496 field.set(v4src, ModelProblemUtils.toId(model));
1497 } catch (Throwable t) {
1498
1499 throw new IllegalStateException("Unable to set modelId on InputSource", t);
1500 }
1501 }
1502 } catch (XmlReaderException e) {
1503 add(
1504 Severity.FATAL,
1505 Version.BASE,
1506 "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage(),
1507 e);
1508 throw newModelBuilderException();
1509 } catch (IOException e) {
1510 String msg = e.getMessage();
1511 if (msg == null || msg.isEmpty()) {
1512
1513 if (e.getClass().getName().endsWith("MalformedInputException")) {
1514 msg = "Some input bytes do not match the file encoding.";
1515 } else {
1516 msg = e.getClass().getSimpleName();
1517 }
1518 }
1519 add(Severity.FATAL, Version.BASE, "Non-readable POM " + modelSource.getLocation() + ": " + msg, e);
1520 throw newModelBuilderException();
1521 }
1522
1523 if (model.getModelVersion() == null) {
1524 String namespace = model.getNamespaceUri();
1525 if (namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) {
1526 model = model.withModelVersion(namespace.substring(NAMESPACE_PREFIX.length()));
1527 }
1528 }
1529
1530 if (isBuildRequest()) {
1531 model = model.withPomFile(modelSource.getPath());
1532
1533 Parent parent = model.getParent();
1534 if (parent != null) {
1535 String groupId = parent.getGroupId();
1536 String artifactId = parent.getArtifactId();
1537 String version = parent.getVersion();
1538 String path = parent.getRelativePath();
1539 if ((groupId == null || artifactId == null || version == null)
1540 && (path == null || !path.isEmpty())) {
1541 Path pomFile = model.getPomFile();
1542 Path relativePath = Paths.get(path != null ? path : "..");
1543 Path pomPath = pomFile.resolveSibling(relativePath).normalize();
1544 if (Files.isDirectory(pomPath)) {
1545 pomPath = modelProcessor.locateExistingPom(pomPath);
1546 }
1547 if (pomPath != null && Files.isRegularFile(pomPath)) {
1548
1549 if (!isParentWithinRootDirectory(pomPath, rootDirectory)) {
1550 add(
1551 Severity.FATAL,
1552 Version.BASE,
1553 "Parent POM " + pomPath + " is located above the root directory "
1554 + rootDirectory
1555 + ". This setup is invalid when a .mvn directory exists in a subdirectory.",
1556 parent.getLocation("relativePath"));
1557 throw newModelBuilderException();
1558 }
1559
1560 Model parentModel =
1561 derive(Sources.buildSource(pomPath)).readFileModel();
1562 String parentGroupId = getGroupId(parentModel);
1563 String parentArtifactId = parentModel.getArtifactId();
1564 String parentVersion = getVersion(parentModel);
1565 if ((groupId == null || groupId.equals(parentGroupId))
1566 && (artifactId == null || artifactId.equals(parentArtifactId))
1567 && (version == null || version.equals(parentVersion))) {
1568 model = model.withParent(parent.with()
1569 .groupId(parentGroupId)
1570 .artifactId(parentArtifactId)
1571 .version(parentVersion)
1572 .build());
1573 } else {
1574 mismatchRelativePathAndGA(model, parentGroupId, parentArtifactId);
1575 }
1576 } else {
1577 if (!MODEL_VERSION_4_0_0.equals(model.getModelVersion()) && path != null) {
1578 wrongParentRelativePath(model);
1579 }
1580 }
1581 }
1582 }
1583
1584
1585 if (!hasSubprojectsDefined(model)
1586
1587 && !MODEL_VERSION_4_0_0.equals(model.getModelVersion())
1588
1589
1590 && Type.POM.equals(model.getPackaging())) {
1591 List<String> subprojects = new ArrayList<>();
1592 try (Stream<Path> files = Files.list(model.getProjectDirectory())) {
1593 for (Path f : files.toList()) {
1594 if (Files.isDirectory(f)) {
1595 Path subproject = modelProcessor.locateExistingPom(f);
1596 if (subproject != null) {
1597 subprojects.add(f.getFileName().toString());
1598 }
1599 }
1600 }
1601 if (!subprojects.isEmpty()) {
1602 model = model.withSubprojects(subprojects);
1603 }
1604 } catch (IOException e) {
1605 add(Severity.FATAL, Version.V41, "Error discovering subprojects", e);
1606 }
1607 }
1608
1609
1610
1611 Map<String, String> properties = getEnhancedProperties(model, rootDirectory);
1612
1613
1614 model = model.with()
1615 .version(replaceCiFriendlyVersion(properties, model.getVersion()))
1616 .parent(
1617 model.getParent() != null
1618 ? model.getParent()
1619 .withVersion(replaceCiFriendlyVersion(
1620 properties,
1621 model.getParent().getVersion()))
1622 : null)
1623 .build();
1624
1625
1626 UnaryOperator<String> callback = properties::get;
1627 model = model.with()
1628 .repositories(interpolateRepository(model.getRepositories(), callback))
1629 .pluginRepositories(interpolateRepository(model.getPluginRepositories(), callback))
1630 .profiles(map(model.getProfiles(), this::interpolateRepository, callback))
1631 .distributionManagement(interpolateRepository(model.getDistributionManagement(), callback))
1632 .build();
1633
1634 Map<String, String> newProps = merge(model.getProperties(), session.getUserProperties());
1635 if (newProps != null) {
1636 model = model.withProperties(newProps);
1637 }
1638 model = model.withProfiles(merge(model.getProfiles(), session.getUserProperties()));
1639 }
1640
1641 for (var transformer : transformers) {
1642 model = transformer.transformFileModel(model);
1643 }
1644
1645 setSource(model);
1646 modelValidator.validateFileModel(
1647 session,
1648 model,
1649 isBuildRequest() ? ModelValidator.VALIDATION_LEVEL_STRICT : ModelValidator.VALIDATION_LEVEL_MINIMAL,
1650 this);
1651 InternalSession internalSession = InternalSession.from(session);
1652 if (Features.mavenMaven3Personality(internalSession.getSession().getConfigProperties())
1653 && Objects.equals(ModelBuilder.MODEL_VERSION_4_1_0, model.getModelVersion())) {
1654 add(Severity.FATAL, Version.BASE, "Maven3 mode: no higher model version than 4.0.0 allowed");
1655 }
1656 if (hasFatalErrors()) {
1657 throw newModelBuilderException();
1658 }
1659
1660 return model;
1661 }
1662
1663 private DistributionManagement interpolateRepository(
1664 DistributionManagement distributionManagement, UnaryOperator<String> callback) {
1665 return distributionManagement == null
1666 ? null
1667 : distributionManagement
1668 .with()
1669 .repository((DeploymentRepository)
1670 interpolateRepository(distributionManagement.getRepository(), callback))
1671 .snapshotRepository((DeploymentRepository)
1672 interpolateRepository(distributionManagement.getSnapshotRepository(), callback))
1673 .build();
1674 }
1675
1676 private Profile interpolateRepository(Profile profile, UnaryOperator<String> callback) {
1677 return profile == null
1678 ? null
1679 : profile.with()
1680 .repositories(interpolateRepository(profile.getRepositories(), callback))
1681 .pluginRepositories(interpolateRepository(profile.getPluginRepositories(), callback))
1682 .build();
1683 }
1684
1685 private List<Repository> interpolateRepository(List<Repository> repositories, UnaryOperator<String> callback) {
1686 return map(repositories, this::interpolateRepository, callback);
1687 }
1688
1689 private Repository interpolateRepository(Repository repository, UnaryOperator<String> callback) {
1690 return repository == null
1691 ? null
1692 : repository
1693 .with()
1694 .id(interpolator.interpolate(repository.getId(), callback))
1695 .url(interpolator.interpolate(repository.getUrl(), callback))
1696 .build();
1697 }
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709 List<Profile> merge(List<Profile> profiles, Map<String, String> userProperties) {
1710 List<Profile> result = null;
1711 for (int i = 0; i < profiles.size(); i++) {
1712 Profile profile = profiles.get(i);
1713 Map<String, String> props = merge(profile.getProperties(), userProperties);
1714 if (props != null) {
1715 Profile merged = profile.withProperties(props);
1716 if (result == null) {
1717 result = new ArrayList<>(profiles);
1718 }
1719 result.set(i, merged);
1720 }
1721 }
1722 return result != null ? result : profiles;
1723 }
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735 Map<String, String> merge(Map<String, String> properties, Map<String, String> userProperties) {
1736 Map<String, String> result = null;
1737 for (Map.Entry<String, String> entry : properties.entrySet()) {
1738 String key = entry.getKey();
1739 String value = userProperties.get(key);
1740 if (value != null && !Objects.equals(value, entry.getValue())) {
1741 if (result == null) {
1742 result = new LinkedHashMap<>(properties);
1743 }
1744 result.put(entry.getKey(), value);
1745 }
1746 }
1747 return result;
1748 }
1749
1750 Model readRawModel() throws ModelBuilderException {
1751
1752 readFileModel();
1753 Model model = cache(request.getSource(), RAW, this::doReadRawModel);
1754
1755 result.setRawModel(model);
1756 return model;
1757 }
1758
1759 private Model doReadRawModel() throws ModelBuilderException {
1760 Model rawModel = readFileModel();
1761
1762 if (!MODEL_VERSION_4_0_0.equals(rawModel.getModelVersion()) && isBuildRequest()) {
1763 rawModel = transformFileToRaw(rawModel);
1764 }
1765
1766 for (var transformer : transformers) {
1767 rawModel = transformer.transformRawModel(rawModel);
1768 }
1769
1770 modelValidator.validateRawModel(
1771 session,
1772 rawModel,
1773 isBuildRequest() ? ModelValidator.VALIDATION_LEVEL_STRICT : ModelValidator.VALIDATION_LEVEL_MINIMAL,
1774 this);
1775
1776 if (hasFatalErrors()) {
1777 throw newModelBuilderException();
1778 }
1779
1780 return rawModel;
1781 }
1782
1783
1784
1785
1786 private record ParentModelWithProfiles(Model model, List<Profile> activatedProfiles) {}
1787
1788
1789
1790
1791 Model readAsParentModel(DefaultProfileActivationContext profileActivationContext, Set<String> parentChain)
1792 throws ModelBuilderException {
1793 Map<DefaultProfileActivationContext.Record, ParentModelWithProfiles> parentsPerContext =
1794 cache(request.getSource(), PARENT, ConcurrentHashMap::new);
1795
1796 for (Map.Entry<DefaultProfileActivationContext.Record, ParentModelWithProfiles> e :
1797 parentsPerContext.entrySet()) {
1798 if (e.getKey().matches(profileActivationContext)) {
1799 ParentModelWithProfiles cached = e.getValue();
1800
1801
1802
1803
1804 if (profileActivationContext.record != null) {
1805 replayRecordIntoContext(e.getKey(), profileActivationContext);
1806 }
1807
1808 addActivePomProfiles(cached.activatedProfiles());
1809 return cached.model();
1810 }
1811 }
1812
1813
1814
1815
1816
1817
1818 DefaultProfileActivationContext ctx = profileActivationContext.start();
1819 ParentModelWithProfiles modelWithProfiles = doReadAsParentModel(ctx, parentChain);
1820 DefaultProfileActivationContext.Record record = ctx.stop();
1821 replayRecordIntoContext(record, profileActivationContext);
1822
1823 parentsPerContext.put(record, modelWithProfiles);
1824 addActivePomProfiles(modelWithProfiles.activatedProfiles());
1825 return modelWithProfiles.model();
1826 }
1827
1828 private ParentModelWithProfiles doReadAsParentModel(
1829 DefaultProfileActivationContext childProfileActivationContext, Set<String> parentChain)
1830 throws ModelBuilderException {
1831 Model raw = readRawModel();
1832 Model parentData = readParent(raw, raw.getParent(), childProfileActivationContext, parentChain);
1833 Model parent = new DefaultInheritanceAssembler(new DefaultInheritanceAssembler.InheritanceModelMerger() {
1834 @Override
1835 protected void mergeModel_Modules(
1836 Model.Builder builder,
1837 Model target,
1838 Model source,
1839 boolean sourceDominant,
1840 Map<Object, Object> context) {}
1841
1842 @Override
1843 protected void mergeModel_Subprojects(
1844 Model.Builder builder,
1845 Model target,
1846 Model source,
1847 boolean sourceDominant,
1848 Map<Object, Object> context) {}
1849 })
1850 .assembleModelInheritance(raw, parentData, request, this);
1851
1852
1853
1854
1855
1856
1857
1858
1859 List<Profile> parentActivePomProfiles =
1860 getActiveProfiles(parent.getProfiles(), childProfileActivationContext);
1861
1862
1863 Model injectedParentModel = profileInjector
1864 .injectProfiles(parent, parentActivePomProfiles, request, this)
1865 .withProfiles(List.of());
1866
1867
1868 return new ParentModelWithProfiles(injectedParentModel.withParent(null), parentActivePomProfiles);
1869 }
1870
1871 private Model importDependencyManagement(Model model, Collection<String> importIds) {
1872 DependencyManagement depMgmt = model.getDependencyManagement();
1873
1874 if (depMgmt == null) {
1875 return model;
1876 }
1877
1878 String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
1879
1880 importIds.add(importing);
1881
1882 List<DependencyManagement> importMgmts = null;
1883
1884 List<Dependency> deps = new ArrayList<>(depMgmt.getDependencies());
1885 for (Iterator<Dependency> it = deps.iterator(); it.hasNext(); ) {
1886 Dependency dependency = it.next();
1887
1888 if (!("pom".equals(dependency.getType()) && "import".equals(dependency.getScope()))
1889 || "bom".equals(dependency.getType())) {
1890 continue;
1891 }
1892
1893 it.remove();
1894
1895 DependencyManagement importMgmt = loadDependencyManagement(dependency, importIds);
1896
1897 if (importMgmt != null) {
1898 if (importMgmts == null) {
1899 importMgmts = new ArrayList<>();
1900 }
1901
1902 importMgmts.add(importMgmt);
1903 }
1904 }
1905
1906 importIds.remove(importing);
1907
1908 model = model.withDependencyManagement(
1909 model.getDependencyManagement().withDependencies(deps));
1910
1911 return dependencyManagementImporter.importManagement(model, importMgmts, request, this);
1912 }
1913
1914 private DependencyManagement loadDependencyManagement(Dependency dependency, Collection<String> importIds) {
1915 String groupId = dependency.getGroupId();
1916 String artifactId = dependency.getArtifactId();
1917 String version = dependency.getVersion();
1918
1919 if (groupId == null || groupId.isEmpty()) {
1920 add(
1921 Severity.ERROR,
1922 Version.BASE,
1923 "'dependencyManagement.dependencies.dependency.groupId' for " + dependency.getManagementKey()
1924 + " is missing.",
1925 dependency.getLocation(""));
1926 return null;
1927 }
1928 if (artifactId == null || artifactId.isEmpty()) {
1929 add(
1930 Severity.ERROR,
1931 Version.BASE,
1932 "'dependencyManagement.dependencies.dependency.artifactId' for " + dependency.getManagementKey()
1933 + " is missing.",
1934 dependency.getLocation(""));
1935 return null;
1936 }
1937 if (version == null || version.isEmpty()) {
1938 add(
1939 Severity.ERROR,
1940 Version.BASE,
1941 "'dependencyManagement.dependencies.dependency.version' for " + dependency.getManagementKey()
1942 + " is missing.",
1943 dependency.getLocation(""));
1944 return null;
1945 }
1946
1947 String imported = groupId + ':' + artifactId + ':' + version;
1948
1949 if (importIds.contains(imported)) {
1950 StringBuilder message =
1951 new StringBuilder("The dependencies of type=pom and with scope=import form a cycle: ");
1952 for (String modelId : importIds) {
1953 message.append(modelId).append(" -> ");
1954 }
1955 message.append(imported);
1956 add(Severity.ERROR, Version.BASE, message.toString());
1957 return null;
1958 }
1959
1960 Model importModel = cache(
1961 repositories,
1962 groupId,
1963 artifactId,
1964 version,
1965 null,
1966 IMPORT,
1967 () -> doLoadDependencyManagement(dependency, groupId, artifactId, version, importIds));
1968 DependencyManagement importMgmt = importModel != null ? importModel.getDependencyManagement() : null;
1969 if (importMgmt == null) {
1970 importMgmt = DependencyManagement.newInstance();
1971 }
1972
1973
1974 List<Exclusion> exclusions = dependency.getExclusions();
1975 if (importMgmt != null && !exclusions.isEmpty()) {
1976
1977 List<Dependency> dependencies = importMgmt.getDependencies().stream()
1978 .filter(candidate -> exclusions.stream().noneMatch(exclusion -> match(exclusion, candidate)))
1979 .map(candidate -> addExclusions(candidate, exclusions))
1980 .collect(Collectors.toList());
1981 importMgmt = importMgmt.withDependencies(dependencies);
1982 }
1983
1984 return importMgmt;
1985 }
1986
1987 @SuppressWarnings("checkstyle:parameternumber")
1988 private Model doLoadDependencyManagement(
1989 Dependency dependency,
1990 String groupId,
1991 String artifactId,
1992 String version,
1993 Collection<String> importIds) {
1994 Model importModel;
1995 ModelSource importSource;
1996 try {
1997 importSource = resolveReactorModel(groupId, artifactId, version);
1998 if (importSource == null) {
1999 importSource = modelResolver.resolveModel(
2000 request.getSession(), repositories, dependency, new AtomicReference<>());
2001 }
2002 } catch (ModelBuilderException | ModelResolverException e) {
2003 StringBuilder buffer = new StringBuilder(256);
2004 buffer.append("Non-resolvable import POM");
2005 if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
2006 buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
2007 }
2008 buffer.append(": ").append(e.getMessage());
2009
2010 add(Severity.ERROR, Version.BASE, buffer.toString(), dependency.getLocation(""), e);
2011 return null;
2012 }
2013
2014 Path rootDirectory;
2015 try {
2016 rootDirectory = request.getSession().getRootDirectory();
2017 } catch (IllegalStateException e) {
2018 rootDirectory = null;
2019 }
2020 if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT && rootDirectory != null) {
2021 Path sourcePath = importSource.getPath();
2022 if (sourcePath != null && sourcePath.startsWith(rootDirectory)) {
2023 add(
2024 Severity.WARNING,
2025 Version.BASE,
2026 "BOM imports from within reactor should be avoided",
2027 dependency.getLocation(""));
2028 }
2029 }
2030
2031 final ModelBuilderResult importResult;
2032 try {
2033 ModelBuilderRequest importRequest = ModelBuilderRequest.builder()
2034 .session(request.getSession())
2035 .requestType(ModelBuilderRequest.RequestType.CONSUMER_DEPENDENCY)
2036 .systemProperties(request.getSystemProperties())
2037 .userProperties(request.getUserProperties())
2038 .source(importSource)
2039 .repositories(repositories)
2040 .build();
2041 ModelBuilderSessionState modelBuilderSession = derive(importRequest);
2042
2043 modelBuilderSession.buildEffectiveModel(importIds);
2044 importResult = modelBuilderSession.result;
2045 } catch (ModelBuilderException e) {
2046 return null;
2047 }
2048
2049 importModel = importResult.getEffectiveModel();
2050
2051 return importModel;
2052 }
2053
2054 ModelSource resolveReactorModel(String groupId, String artifactId, String version)
2055 throws ModelBuilderException {
2056 Set<ModelSource> sources = mappedSources.get(new GAKey(groupId, artifactId));
2057 if (sources != null) {
2058 for (ModelSource source : sources) {
2059 Model model = derive(source).readRawModel();
2060 if (Objects.equals(getVersion(model), version)) {
2061 return source;
2062 }
2063 }
2064
2065 }
2066 return null;
2067 }
2068
2069 private <T> T cache(
2070 List<RemoteRepository> repositories,
2071 String groupId,
2072 String artifactId,
2073 String version,
2074 String classifier,
2075 String tag,
2076 Supplier<T> supplier) {
2077 RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
2078 try {
2079 RgavCacheKey r = new RgavCacheKey(
2080 session, trace.mvnTrace(), repositories, groupId, artifactId, version, classifier, tag);
2081 SourceResponse<RgavCacheKey, T> response =
2082 InternalSession.from(session).request(r, tr -> new SourceResponse<>(tr, supplier.get()));
2083 return response.response;
2084 } finally {
2085 RequestTraceHelper.exit(trace);
2086 }
2087 }
2088
2089 private <T> T cache(Source source, String tag, Supplier<T> supplier) throws ModelBuilderException {
2090 RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
2091 try {
2092 SourceCacheKey r = new SourceCacheKey(session, trace.mvnTrace(), source, tag);
2093 SourceResponse<SourceCacheKey, T> response =
2094 InternalSession.from(session).request(r, tr -> new SourceResponse<>(tr, supplier.get()));
2095 return response.response;
2096 } finally {
2097 RequestTraceHelper.exit(trace);
2098 }
2099 }
2100
2101 boolean isBuildRequest() {
2102 return request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT
2103 || request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_EFFECTIVE
2104 || request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_CONSUMER;
2105 }
2106
2107 boolean isBuildRequestWithActivation() {
2108 return request.getRequestType() != ModelBuilderRequest.RequestType.BUILD_CONSUMER;
2109 }
2110
2111
2112
2113
2114
2115
2116 private void replayRecordIntoContext(
2117 DefaultProfileActivationContext.Record cachedRecord, DefaultProfileActivationContext targetContext) {
2118 if (targetContext.record == null) {
2119 return;
2120 }
2121
2122
2123
2124 DefaultProfileActivationContext.Record targetRecord = targetContext.record;
2125
2126
2127 cachedRecord.usedActiveProfiles.forEach(targetRecord.usedActiveProfiles::putIfAbsent);
2128
2129
2130 cachedRecord.usedInactiveProfiles.forEach(targetRecord.usedInactiveProfiles::putIfAbsent);
2131
2132
2133 cachedRecord.usedSystemProperties.forEach(targetRecord.usedSystemProperties::putIfAbsent);
2134
2135
2136 cachedRecord.usedUserProperties.forEach(targetRecord.usedUserProperties::putIfAbsent);
2137
2138
2139 cachedRecord.usedModelProperties.forEach(targetRecord.usedModelProperties::putIfAbsent);
2140
2141
2142 cachedRecord.usedModelInfos.forEach(targetRecord.usedModelInfos::putIfAbsent);
2143
2144
2145 cachedRecord.usedExists.forEach(targetRecord.usedExists::putIfAbsent);
2146 }
2147 }
2148
2149 @SuppressWarnings("deprecation")
2150 private static List<String> getSubprojects(Model activated) {
2151 List<String> subprojects = activated.getSubprojects();
2152 if (subprojects.isEmpty()) {
2153 subprojects = activated.getModules();
2154 }
2155 return subprojects;
2156 }
2157
2158
2159
2160
2161
2162
2163
2164
2165 @SuppressWarnings("deprecation")
2166 private static boolean hasSubprojectsDefined(Model model) {
2167
2168
2169 return model.getLocation("subprojects") != null || model.getLocation("modules") != null;
2170 }
2171
2172 @Override
2173 public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException {
2174 RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(request.getSession(), request);
2175 try {
2176 ModelBuilderSessionState build = new ModelBuilderSessionState(request);
2177 Model model = build.readRawModel();
2178 if (build.hasErrors()) {
2179 throw build.newModelBuilderException();
2180 }
2181 return model;
2182 } finally {
2183 RequestTraceHelper.exit(trace);
2184 }
2185 }
2186
2187 static String getGroupId(Model model) {
2188 String groupId = model.getGroupId();
2189 if (groupId == null && model.getParent() != null) {
2190 groupId = model.getParent().getGroupId();
2191 }
2192 return groupId;
2193 }
2194
2195 static String getVersion(Model model) {
2196 String version = model.getVersion();
2197 if (version == null && model.getParent() != null) {
2198 version = model.getParent().getVersion();
2199 }
2200 return version;
2201 }
2202
2203 private DefaultProfileActivationContext getProfileActivationContext(ModelBuilderRequest request, Model model) {
2204 return new DefaultProfileActivationContext(
2205 pathTranslator,
2206 rootLocator,
2207 interpolator,
2208 request.getActiveProfileIds(),
2209 request.getInactiveProfileIds(),
2210 request.getSystemProperties(),
2211 request.getUserProperties(),
2212 model);
2213 }
2214
2215 private Map<String, Activation> getProfileActivations(Model model) {
2216 return model.getProfiles().stream()
2217 .filter(p -> p.getActivation() != null)
2218 .collect(Collectors.toMap(Profile::getId, Profile::getActivation));
2219 }
2220
2221 private Model injectProfileActivations(Model model, Map<String, Activation> activations) {
2222 List<Profile> profiles = new ArrayList<>();
2223 boolean modified = false;
2224 for (Profile profile : model.getProfiles()) {
2225 Activation activation = profile.getActivation();
2226 if (activation != null) {
2227
2228 profile = profile.withActivation(activations.get(profile.getId()));
2229 modified = true;
2230 }
2231 profiles.add(profile);
2232 }
2233 return modified ? model.withProfiles(profiles) : model;
2234 }
2235
2236 private Model interpolateModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
2237 Model interpolatedModel =
2238 modelInterpolator.interpolateModel(model, model.getProjectDirectory(), request, problems);
2239 if (interpolatedModel.getParent() != null) {
2240 Map<String, String> map1 = request.getSession().getUserProperties();
2241 Map<String, String> map2 = model.getProperties();
2242 Map<String, String> map3 = request.getSession().getSystemProperties();
2243 UnaryOperator<String> cb = Interpolator.chain(map1::get, map2::get, map3::get);
2244 try {
2245 String interpolated =
2246 interpolator.interpolate(interpolatedModel.getParent().getVersion(), cb);
2247 interpolatedModel = interpolatedModel.withParent(
2248 interpolatedModel.getParent().withVersion(interpolated));
2249 } catch (Exception e) {
2250 problems.add(
2251 Severity.ERROR,
2252 Version.BASE,
2253 "Failed to interpolate field: "
2254 + interpolatedModel.getParent().getVersion()
2255 + " on class: ",
2256 e);
2257 }
2258 }
2259 interpolatedModel = interpolatedModel.withPomFile(model.getPomFile());
2260 return interpolatedModel;
2261 }
2262
2263 private boolean rawChildVersionReferencesParent(String rawChildModelVersion) {
2264 return rawChildModelVersion.equals("${pom.version}")
2265 || rawChildModelVersion.equals("${project.version}")
2266 || rawChildModelVersion.equals("${pom.parent.version}")
2267 || rawChildModelVersion.equals("${project.parent.version}");
2268 }
2269
2270 private Model getSuperModel(String modelVersion) {
2271 return superPomProvider.getSuperPom(modelVersion);
2272 }
2273
2274 private static org.apache.maven.api.model.Dependency addExclusions(
2275 org.apache.maven.api.model.Dependency candidate, List<Exclusion> exclusions) {
2276 return candidate.withExclusions(Stream.concat(candidate.getExclusions().stream(), exclusions.stream())
2277 .toList());
2278 }
2279
2280 private boolean match(Exclusion exclusion, Dependency candidate) {
2281 return match(exclusion.getGroupId(), candidate.getGroupId())
2282 && match(exclusion.getArtifactId(), candidate.getArtifactId());
2283 }
2284
2285 private boolean match(String match, String text) {
2286 return match.equals("*") || match.equals(text);
2287 }
2288
2289 private boolean containsCoordinates(String message, String groupId, String artifactId, String version) {
2290 return message != null
2291 && (groupId == null || message.contains(groupId))
2292 && (artifactId == null || message.contains(artifactId))
2293 && (version == null || message.contains(version));
2294 }
2295
2296 record GAKey(String groupId, String artifactId) {}
2297
2298 public record RgavCacheKey(
2299 Session session,
2300 RequestTrace trace,
2301 List<RemoteRepository> repositories,
2302 String groupId,
2303 String artifactId,
2304 String version,
2305 String classifier,
2306 String tag)
2307 implements Request<Session> {
2308 @Nonnull
2309 @Override
2310 public Session getSession() {
2311 return session;
2312 }
2313
2314 @Nullable
2315 @Override
2316 public RequestTrace getTrace() {
2317 return trace;
2318 }
2319
2320 @Override
2321 public boolean equals(Object o) {
2322 return o instanceof RgavCacheKey that
2323 && Objects.equals(tag, that.tag)
2324 && Objects.equals(groupId, that.groupId)
2325 && Objects.equals(version, that.version)
2326 && Objects.equals(artifactId, that.artifactId)
2327 && Objects.equals(classifier, that.classifier)
2328 && Objects.equals(repositories, that.repositories);
2329 }
2330
2331 @Override
2332 public int hashCode() {
2333 return Objects.hash(repositories, groupId, artifactId, version, classifier, tag);
2334 }
2335
2336 @Override
2337 public String toString() {
2338 StringBuilder sb = new StringBuilder();
2339 sb.append(getClass().getSimpleName()).append("[").append("gav='");
2340 if (groupId != null) {
2341 sb.append(groupId);
2342 }
2343 sb.append(":");
2344 if (artifactId != null) {
2345 sb.append(artifactId);
2346 }
2347 sb.append(":");
2348 if (version != null) {
2349 sb.append(version);
2350 }
2351 sb.append(":");
2352 if (classifier != null) {
2353 sb.append(classifier);
2354 }
2355 sb.append("', tag='");
2356 sb.append(tag);
2357 sb.append("']");
2358 return sb.toString();
2359 }
2360 }
2361
2362 public record SourceCacheKey(Session session, RequestTrace trace, Source source, String tag)
2363 implements Request<Session>, CacheMetadata {
2364 @Nonnull
2365 @Override
2366 public Session getSession() {
2367 return session;
2368 }
2369
2370 @Nullable
2371 @Override
2372 public RequestTrace getTrace() {
2373 return trace;
2374 }
2375
2376 @Override
2377 public CacheRetention getCacheRetention() {
2378 return source instanceof CacheMetadata cacheMetadata ? cacheMetadata.getCacheRetention() : null;
2379 }
2380
2381 @Override
2382 public boolean equals(Object o) {
2383 return o instanceof SourceCacheKey that
2384 && Objects.equals(tag, that.tag)
2385 && Objects.equals(source, that.source);
2386 }
2387
2388 @Override
2389 public int hashCode() {
2390 return Objects.hash(source, tag);
2391 }
2392
2393 @Override
2394 public String toString() {
2395 return getClass().getSimpleName() + "[" + "location=" + source.getLocation() + ", tag=" + tag + ", path="
2396 + source.getPath() + ']';
2397 }
2398 }
2399
2400 public static class SourceResponse<R extends Request<?>, T> implements Result<R> {
2401 private final R request;
2402 private final T response;
2403
2404 SourceResponse(R request, T response) {
2405 this.request = request;
2406 this.response = response;
2407 }
2408
2409 @Nonnull
2410 @Override
2411 public R getRequest() {
2412 return request;
2413 }
2414 }
2415
2416 static class InterningTransformer implements XmlReaderRequest.Transformer {
2417 static final Set<String> DEFAULT_CONTEXTS = Set.of(
2418
2419 "groupId",
2420 "artifactId",
2421 "version",
2422 "namespaceUri",
2423 "packaging",
2424
2425
2426 "scope",
2427 "type",
2428 "classifier",
2429
2430
2431 "phase",
2432 "goal",
2433 "execution",
2434
2435
2436 "layout",
2437 "policy",
2438 "checksumPolicy",
2439 "updatePolicy",
2440
2441
2442 "modelVersion",
2443 "name",
2444 "url",
2445 "system",
2446 "distribution",
2447 "status",
2448
2449
2450 "connection",
2451 "developerConnection",
2452 "tag",
2453
2454
2455 "id",
2456 "inherited",
2457 "optional");
2458
2459 private final Set<String> contexts;
2460
2461
2462
2463
2464 InterningTransformer() {
2465 this.contexts = DEFAULT_CONTEXTS;
2466 }
2467
2468
2469
2470
2471
2472
2473 InterningTransformer(Session session) {
2474 this.contexts = parseContextsFromSession(session);
2475 }
2476
2477 private Set<String> parseContextsFromSession(Session session) {
2478 String contextsProperty = session.getUserProperties().get(Constants.MAVEN_MODEL_BUILDER_INTERNS);
2479 if (contextsProperty == null) {
2480 contextsProperty = session.getSystemProperties().get(Constants.MAVEN_MODEL_BUILDER_INTERNS);
2481 }
2482
2483 if (contextsProperty == null || contextsProperty.trim().isEmpty()) {
2484 return DEFAULT_CONTEXTS;
2485 }
2486
2487 return Arrays.stream(contextsProperty.split(","))
2488 .map(String::trim)
2489 .filter(s -> !s.isEmpty())
2490 .collect(Collectors.toSet());
2491 }
2492
2493 @Override
2494 public String transform(String input, String context) {
2495 return input != null && contexts.contains(context) ? input.intern() : input;
2496 }
2497
2498
2499
2500
2501
2502 Set<String> getContexts() {
2503 return contexts;
2504 }
2505 }
2506
2507 private static <T, A> List<T> map(List<T> resources, BiFunction<T, A, T> mapper, A argument) {
2508 List<T> newResources = null;
2509 if (resources != null) {
2510 for (int i = 0; i < resources.size(); i++) {
2511 T resource = resources.get(i);
2512 T newResource = mapper.apply(resource, argument);
2513 if (newResource != resource) {
2514 if (newResources == null) {
2515 newResources = new ArrayList<>(resources);
2516 }
2517 newResources.set(i, newResource);
2518 }
2519 }
2520 }
2521 return newResources;
2522 }
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532 private static boolean isParentWithinRootDirectory(Path parentPath, Path rootDirectory) {
2533 if (parentPath == null || rootDirectory == null) {
2534 return true;
2535 }
2536
2537 try {
2538 Path normalizedParent = parentPath.toAbsolutePath().normalize();
2539 Path normalizedRoot = rootDirectory.toAbsolutePath().normalize();
2540
2541
2542 return normalizedParent.startsWith(normalizedRoot);
2543 } catch (Exception e) {
2544
2545 return true;
2546 }
2547 }
2548 }