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