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 addActivePomProfiles(derived.result.getActivePomProfiles());
1137
1138 String groupId = getGroupId(candidateModel);
1139 String artifactId = candidateModel.getArtifactId();
1140 String version = getVersion(candidateModel);
1141
1142
1143 if (parent.getGroupId() != null && (groupId == null || !groupId.equals(parent.getGroupId()))
1144 || parent.getArtifactId() != null
1145 && (artifactId == null || !artifactId.equals(parent.getArtifactId()))) {
1146 mismatchRelativePathAndGA(childModel, parent, groupId, artifactId);
1147 return null;
1148 }
1149
1150 if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) {
1151 try {
1152 VersionRange parentRange = versionParser.parseVersionRange(parent.getVersion());
1153 if (!parentRange.contains(versionParser.parseVersion(version))) {
1154
1155 return null;
1156 }
1157
1158
1159
1160 String rawChildModelVersion = childModel.getVersion();
1161
1162 if (rawChildModelVersion == null) {
1163
1164 add(Severity.FATAL, Version.V31, "Version must be a constant", childModel.getLocation(""));
1165
1166 } else {
1167 if (rawChildVersionReferencesParent(rawChildModelVersion)) {
1168
1169 add(
1170 Severity.FATAL,
1171 Version.V31,
1172 "Version must be a constant",
1173 childModel.getLocation("version"));
1174 }
1175 }
1176
1177
1178 } catch (VersionParserException e) {
1179
1180 return null;
1181 }
1182 }
1183 return candidateModel;
1184 } finally {
1185
1186 parentChain.remove(sourceLocation);
1187 }
1188 }
1189
1190 private void mismatchRelativePathAndGA(Model childModel, Parent parent, String groupId, String artifactId) {
1191 StringBuilder buffer = new StringBuilder(256);
1192 buffer.append("'parent.relativePath'");
1193 if (childModel != getRootModel()) {
1194 buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel));
1195 }
1196 buffer.append(" points at ").append(groupId).append(':').append(artifactId);
1197 buffer.append(" instead of ").append(parent.getGroupId()).append(':');
1198 buffer.append(parent.getArtifactId()).append(", please verify your project structure");
1199
1200 setSource(childModel);
1201 boolean warn = MODEL_VERSION_4_0_0.equals(childModel.getModelVersion())
1202 || childModel.getParent().getRelativePath() == null;
1203 add(warn ? Severity.WARNING : Severity.FATAL, Version.BASE, buffer.toString(), parent.getLocation(""));
1204 }
1205
1206 private void wrongParentRelativePath(Model childModel) {
1207 Parent parent = childModel.getParent();
1208 String parentPath = parent.getRelativePath();
1209 StringBuilder buffer = new StringBuilder(256);
1210 buffer.append("'parent.relativePath'");
1211 if (childModel != getRootModel()) {
1212 buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel));
1213 }
1214 buffer.append(" points at '").append(parentPath);
1215 buffer.append("' but no POM could be found, please verify your project structure");
1216
1217 setSource(childModel);
1218 add(Severity.FATAL, Version.BASE, buffer.toString(), parent.getLocation(""));
1219 }
1220
1221 Model resolveAndReadParentExternally(
1222 Model childModel,
1223 Parent parent,
1224 DefaultProfileActivationContext profileActivationContext,
1225 Set<String> parentChain)
1226 throws ModelBuilderException {
1227 ModelBuilderRequest request = this.request;
1228 setSource(childModel);
1229
1230 String groupId = parent.getGroupId();
1231 String artifactId = parent.getArtifactId();
1232 String version = parent.getVersion();
1233 String classifier = parent instanceof Mixin ? ((Mixin) parent).getClassifier() : null;
1234 String extension = parent instanceof Mixin ? ((Mixin) parent).getExtension() : null;
1235
1236
1237 if (!childModel.getRepositories().isEmpty()) {
1238 var previousRepositories = repositories;
1239 mergeRepositories(childModel, false);
1240 if (!Objects.equals(previousRepositories, repositories)) {
1241 if (logger.isDebugEnabled()) {
1242 logger.debug("Merging repositories from " + childModel.getId() + "\n"
1243 + repositories.stream()
1244 .map(Object::toString)
1245 .collect(Collectors.joining("\n", " ", "")));
1246 }
1247 }
1248 }
1249
1250 ModelSource modelSource;
1251 try {
1252 modelSource = classifier == null && extension == null
1253 ? resolveReactorModel(groupId, artifactId, version)
1254 : null;
1255 if (modelSource == null) {
1256 ModelResolver.ModelResolverRequest req = new ModelResolver.ModelResolverRequest(
1257 request.getSession(),
1258 null,
1259 repositories,
1260 groupId,
1261 artifactId,
1262 version,
1263 classifier,
1264 extension != null ? extension : "pom");
1265 ModelResolver.ModelResolverResult result = modelResolver.resolveModel(req);
1266 modelSource = result.source();
1267 if (result.version() != null) {
1268 parent = parent.withVersion(result.version());
1269 }
1270 }
1271 } catch (ModelResolverException e) {
1272
1273 StringBuilder buffer = new StringBuilder(256);
1274 buffer.append("Non-resolvable parent POM");
1275 if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
1276 buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
1277 }
1278 if (childModel != getRootModel()) {
1279 buffer.append(" for ").append(ModelProblemUtils.toId(childModel));
1280 }
1281 buffer.append(": ").append(e.getMessage());
1282 if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) {
1283 buffer.append(" and parent could not be found in reactor");
1284 }
1285
1286 add(Severity.FATAL, Version.BASE, buffer.toString(), parent.getLocation(""), e);
1287 throw newModelBuilderException();
1288 }
1289
1290 ModelBuilderRequest lenientRequest = ModelBuilderRequest.builder(request)
1291 .requestType(ModelBuilderRequest.RequestType.CONSUMER_PARENT)
1292 .source(modelSource)
1293 .build();
1294
1295 Model parentModel = derive(lenientRequest).readAsParentModel(profileActivationContext, parentChain);
1296
1297 if (!parent.getVersion().equals(version)) {
1298 String rawChildModelVersion = childModel.getVersion();
1299
1300 if (rawChildModelVersion == null) {
1301
1302 add(Severity.FATAL, Version.V31, "Version must be a constant", childModel.getLocation(""));
1303 } else {
1304 if (rawChildVersionReferencesParent(rawChildModelVersion)) {
1305
1306 add(
1307 Severity.FATAL,
1308 Version.V31,
1309 "Version must be a constant",
1310 childModel.getLocation("version"));
1311 }
1312 }
1313
1314
1315 }
1316
1317 return parentModel;
1318 }
1319
1320 Model activateFileModel(Model inputModel) throws ModelBuilderException {
1321 setRootModel(inputModel);
1322
1323
1324 DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel);
1325
1326 setSource("(external profiles)");
1327 List<Profile> activeExternalProfiles = getActiveProfiles(request.getProfiles(), profileActivationContext);
1328
1329 result.setActiveExternalProfiles(activeExternalProfiles);
1330
1331 if (!activeExternalProfiles.isEmpty()) {
1332 Map<String, String> profileProps = new HashMap<>();
1333 for (Profile profile : activeExternalProfiles) {
1334 profileProps.putAll(profile.getProperties());
1335 }
1336 profileProps.putAll(request.getUserProperties());
1337 profileActivationContext.setUserProperties(profileProps);
1338 }
1339
1340 profileActivationContext.setModel(inputModel);
1341 setSource(inputModel);
1342 List<Profile> activePomProfiles = getActiveProfiles(inputModel.getProfiles(), profileActivationContext);
1343
1344
1345 setSource(inputModel);
1346 inputModel = modelNormalizer.mergeDuplicates(inputModel, request, this);
1347
1348 Map<String, Activation> interpolatedActivations = getProfileActivations(inputModel);
1349 inputModel = injectProfileActivations(inputModel, interpolatedActivations);
1350
1351
1352 inputModel = profileInjector.injectProfiles(inputModel, activePomProfiles, request, this);
1353 inputModel = profileInjector.injectProfiles(inputModel, activeExternalProfiles, request, this);
1354
1355 return inputModel;
1356 }
1357
1358 @SuppressWarnings("checkstyle:methodlength")
1359 private Model readEffectiveModel() throws ModelBuilderException {
1360 Model inputModel = readRawModel();
1361 if (hasFatalErrors()) {
1362 throw newModelBuilderException();
1363 }
1364
1365 setRootModel(inputModel);
1366
1367 Model activatedFileModel = activateFileModel(inputModel);
1368
1369
1370 DefaultProfileActivationContext profileActivationContext =
1371 getProfileActivationContext(request, activatedFileModel);
1372
1373 List<Profile> activeExternalProfiles = result.getActiveExternalProfiles();
1374
1375 if (!activeExternalProfiles.isEmpty()) {
1376 Map<String, String> profileProps = new HashMap<>();
1377 for (Profile profile : activeExternalProfiles) {
1378 profileProps.putAll(profile.getProperties());
1379 }
1380 profileProps.putAll(request.getUserProperties());
1381 profileActivationContext.setUserProperties(profileProps);
1382 }
1383
1384 Model parentModel = readParent(
1385 activatedFileModel, activatedFileModel.getParent(), profileActivationContext, parentChain);
1386
1387
1388
1389 if (inputModel.getParent() != null && inputModel.getParent().getRelativePath() == null) {
1390 String relPath;
1391 if (parentModel.getPomFile() != null && isBuildRequest()) {
1392 relPath = inputModel
1393 .getPomFile()
1394 .getParent()
1395 .toAbsolutePath()
1396 .relativize(
1397 parentModel.getPomFile().toAbsolutePath().getParent())
1398 .toString();
1399 } else {
1400 relPath = "..";
1401 }
1402 inputModel = inputModel.withParent(inputModel.getParent().withRelativePath(relPath));
1403 }
1404
1405 Model model = inheritanceAssembler.assembleModelInheritance(inputModel, parentModel, request, this);
1406
1407
1408 for (Mixin mixin : model.getMixins()) {
1409 Model parent = resolveParent(model, mixin, profileActivationContext, parentChain);
1410 model = inheritanceAssembler.assembleModelInheritance(model, parent, request, this);
1411 }
1412
1413
1414 model = modelNormalizer.mergeDuplicates(model, request, this);
1415
1416
1417 profileActivationContext.setModel(model);
1418
1419
1420 List<Profile> activePomProfiles = getActiveProfiles(model.getProfiles(), profileActivationContext);
1421 model = profileInjector.injectProfiles(model, activePomProfiles, request, this);
1422 model = profileInjector.injectProfiles(model, activeExternalProfiles, request, this);
1423
1424 addActivePomProfiles(activePomProfiles);
1425
1426
1427 Model resultModel = model;
1428 resultModel = interpolateModel(resultModel, request, this);
1429
1430
1431 resultModel = modelNormalizer.mergeDuplicates(resultModel, request, this);
1432
1433
1434 resultModel = modelUrlNormalizer.normalize(resultModel, request);
1435
1436
1437 if (!resultModel.getRepositories().isEmpty()) {
1438 List<String> oldRepos =
1439 repositories.stream().map(Object::toString).toList();
1440 mergeRepositories(resultModel, true);
1441 List<String> newRepos =
1442 repositories.stream().map(Object::toString).toList();
1443 if (!Objects.equals(oldRepos, newRepos)) {
1444 logger.debug("Replacing repositories from " + resultModel.getId() + "\n"
1445 + newRepos.stream().map(s -> " " + s).collect(Collectors.joining("\n")));
1446 }
1447 }
1448
1449 return resultModel;
1450 }
1451
1452 private void addActivePomProfiles(List<Profile> activePomProfiles) {
1453 if (activePomProfiles != null) {
1454 if (result.getActivePomProfiles() == null) {
1455 result.setActivePomProfiles(new ArrayList<>());
1456 }
1457 result.getActivePomProfiles().addAll(activePomProfiles);
1458 }
1459 }
1460
1461 private List<Profile> getActiveProfiles(
1462 Collection<Profile> interpolatedProfiles, DefaultProfileActivationContext profileActivationContext) {
1463 if (isBuildRequestWithActivation()) {
1464 return profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, this);
1465 } else {
1466 return List.of();
1467 }
1468 }
1469
1470 Model readFileModel() throws ModelBuilderException {
1471 Model model = cache(request.getSource(), FILE, this::doReadFileModel);
1472
1473 result.setFileModel(model);
1474 return model;
1475 }
1476
1477 @SuppressWarnings("checkstyle:methodlength")
1478 Model doReadFileModel() throws ModelBuilderException {
1479 ModelSource modelSource = request.getSource();
1480 Model model;
1481 Path rootDirectory;
1482 setSource(modelSource.getLocation());
1483 logger.debug("Reading file model from " + modelSource.getLocation());
1484 try {
1485 boolean strict = isBuildRequest();
1486 try {
1487 rootDirectory = request.getSession().getRootDirectory();
1488 } catch (IllegalStateException ignore) {
1489 rootDirectory = modelSource.getPath();
1490 while (rootDirectory != null && !Files.isDirectory(rootDirectory)) {
1491 rootDirectory = rootDirectory.getParent();
1492 }
1493 }
1494 try (InputStream is = modelSource.openStream()) {
1495 model = modelProcessor.read(XmlReaderRequest.builder()
1496 .strict(strict)
1497 .location(modelSource.getLocation())
1498 .modelId(modelSource.getModelId())
1499 .path(modelSource.getPath())
1500 .rootDirectory(rootDirectory)
1501 .inputStream(is)
1502 .transformer(new InterningTransformer(session))
1503 .build());
1504 } catch (XmlReaderException e) {
1505 if (!strict) {
1506 throw e;
1507 }
1508 try (InputStream is = modelSource.openStream()) {
1509 model = modelProcessor.read(XmlReaderRequest.builder()
1510 .strict(false)
1511 .location(modelSource.getLocation())
1512 .modelId(modelSource.getModelId())
1513 .path(modelSource.getPath())
1514 .rootDirectory(rootDirectory)
1515 .inputStream(is)
1516 .transformer(new InterningTransformer(session))
1517 .build());
1518 } catch (XmlReaderException ne) {
1519
1520 throw e;
1521 }
1522
1523 add(
1524 Severity.ERROR,
1525 Version.V20,
1526 "Malformed POM " + modelSource.getLocation() + ": " + e.getMessage(),
1527 e);
1528 }
1529 } catch (XmlReaderException e) {
1530 add(
1531 Severity.FATAL,
1532 Version.BASE,
1533 "Non-parseable POM " + modelSource.getLocation() + ": " + e.getMessage(),
1534 e);
1535 throw newModelBuilderException();
1536 } catch (IOException e) {
1537 String msg = e.getMessage();
1538 if (msg == null || msg.isEmpty()) {
1539
1540 if (e.getClass().getName().endsWith("MalformedInputException")) {
1541 msg = "Some input bytes do not match the file encoding.";
1542 } else {
1543 msg = e.getClass().getSimpleName();
1544 }
1545 }
1546 add(Severity.FATAL, Version.BASE, "Non-readable POM " + modelSource.getLocation() + ": " + msg, e);
1547 throw newModelBuilderException();
1548 }
1549
1550 if (model.getModelVersion() == null) {
1551 String namespace = model.getNamespaceUri();
1552 if (namespace != null && namespace.startsWith(NAMESPACE_PREFIX)) {
1553 model = model.withModelVersion(namespace.substring(NAMESPACE_PREFIX.length()));
1554 }
1555 }
1556
1557 if (isBuildRequest()) {
1558 model = model.withPomFile(modelSource.getPath());
1559
1560 Parent parent = model.getParent();
1561 if (parent != null) {
1562 String groupId = parent.getGroupId();
1563 String artifactId = parent.getArtifactId();
1564 String version = parent.getVersion();
1565 String path = parent.getRelativePath();
1566 if ((groupId == null || artifactId == null || version == null)
1567 && (path == null || !path.isEmpty())) {
1568 Path pomFile = model.getPomFile();
1569 Path relativePath = Paths.get(path != null ? path : "..");
1570 Path pomPath = pomFile.resolveSibling(relativePath).normalize();
1571 if (Files.isDirectory(pomPath)) {
1572 pomPath = modelProcessor.locateExistingPom(pomPath);
1573 }
1574 if (pomPath != null && Files.isRegularFile(pomPath)) {
1575
1576 if (!isParentWithinRootDirectory(pomPath, rootDirectory)) {
1577 add(
1578 Severity.FATAL,
1579 Version.BASE,
1580 "Parent POM " + pomPath + " is located above the root directory "
1581 + rootDirectory
1582 + ". This setup is invalid when a .mvn directory exists in a subdirectory.",
1583 parent.getLocation("relativePath"));
1584 throw newModelBuilderException();
1585 }
1586
1587 Model parentModel =
1588 derive(Sources.buildSource(pomPath)).readFileModel();
1589 String parentGroupId = getGroupId(parentModel);
1590 String parentArtifactId = parentModel.getArtifactId();
1591 String parentVersion = getVersion(parentModel);
1592 if ((groupId == null || groupId.equals(parentGroupId))
1593 && (artifactId == null || artifactId.equals(parentArtifactId))
1594 && (version == null || version.equals(parentVersion))) {
1595 model = model.withParent(parent.with()
1596 .groupId(parentGroupId)
1597 .artifactId(parentArtifactId)
1598 .version(parentVersion)
1599 .build());
1600 } else {
1601 mismatchRelativePathAndGA(model, parent, parentGroupId, parentArtifactId);
1602 }
1603 } else {
1604 if (!MODEL_VERSION_4_0_0.equals(model.getModelVersion()) && path != null) {
1605 wrongParentRelativePath(model);
1606 }
1607 }
1608 }
1609 }
1610
1611
1612 if (!hasSubprojectsDefined(model)
1613
1614 && !MODEL_VERSION_4_0_0.equals(model.getModelVersion())
1615
1616
1617 && Type.POM.equals(model.getPackaging())) {
1618 List<String> subprojects = new ArrayList<>();
1619 try (Stream<Path> files = Files.list(model.getProjectDirectory())) {
1620 for (Path f : files.toList()) {
1621 if (Files.isDirectory(f)) {
1622 Path subproject = modelProcessor.locateExistingPom(f);
1623 if (subproject != null) {
1624 subprojects.add(f.getFileName().toString());
1625 }
1626 }
1627 }
1628 if (!subprojects.isEmpty()) {
1629 model = model.withSubprojects(subprojects);
1630 }
1631 } catch (IOException e) {
1632 add(Severity.FATAL, Version.V41, "Error discovering subprojects", e);
1633 }
1634 }
1635
1636
1637
1638 Map<String, String> properties = getEnhancedProperties(model, rootDirectory);
1639
1640
1641 model = model.with()
1642 .version(replaceCiFriendlyVersion(properties, model.getVersion()))
1643 .parent(
1644 model.getParent() != null
1645 ? model.getParent()
1646 .withVersion(replaceCiFriendlyVersion(
1647 properties,
1648 model.getParent().getVersion()))
1649 : null)
1650 .build();
1651
1652
1653 UnaryOperator<String> callback = properties::get;
1654 model = model.with()
1655 .repositories(interpolateRepository(model.getRepositories(), callback))
1656 .pluginRepositories(interpolateRepository(model.getPluginRepositories(), callback))
1657 .profiles(map(model.getProfiles(), this::interpolateRepository, callback))
1658 .distributionManagement(interpolateRepository(model.getDistributionManagement(), callback))
1659 .build();
1660
1661 Map<String, String> newProps = merge(model.getProperties(), session.getUserProperties());
1662 if (newProps != null) {
1663 model = model.withProperties(newProps);
1664 }
1665 model = model.withProfiles(merge(model.getProfiles(), session.getUserProperties()));
1666 }
1667
1668 for (var transformer : transformers) {
1669 model = transformer.transformFileModel(model);
1670 }
1671
1672 setSource(model);
1673 modelValidator.validateFileModel(
1674 session,
1675 model,
1676 isBuildRequest() ? ModelValidator.VALIDATION_LEVEL_STRICT : ModelValidator.VALIDATION_LEVEL_MINIMAL,
1677 this);
1678 InternalSession internalSession = InternalSession.from(session);
1679 if (Features.mavenMaven3Personality(internalSession.getSession().getConfigProperties())
1680 && Objects.equals(ModelBuilder.MODEL_VERSION_4_1_0, model.getModelVersion())) {
1681 add(Severity.FATAL, Version.BASE, "Maven3 mode: no higher model version than 4.0.0 allowed");
1682 }
1683 if (hasFatalErrors()) {
1684 throw newModelBuilderException();
1685 }
1686
1687 return model;
1688 }
1689
1690 private DistributionManagement interpolateRepository(
1691 DistributionManagement distributionManagement, UnaryOperator<String> callback) {
1692 return distributionManagement == null
1693 ? null
1694 : distributionManagement
1695 .with()
1696 .repository((DeploymentRepository)
1697 interpolateRepository(distributionManagement.getRepository(), callback))
1698 .snapshotRepository((DeploymentRepository)
1699 interpolateRepository(distributionManagement.getSnapshotRepository(), callback))
1700 .build();
1701 }
1702
1703 private Profile interpolateRepository(Profile profile, UnaryOperator<String> callback) {
1704 return profile == null
1705 ? null
1706 : profile.with()
1707 .repositories(interpolateRepository(profile.getRepositories(), callback))
1708 .pluginRepositories(interpolateRepository(profile.getPluginRepositories(), callback))
1709 .build();
1710 }
1711
1712 private List<Repository> interpolateRepository(List<Repository> repositories, UnaryOperator<String> callback) {
1713 return map(repositories, this::interpolateRepository, callback);
1714 }
1715
1716 private Repository interpolateRepository(Repository repository, UnaryOperator<String> callback) {
1717 return repository == null
1718 ? null
1719 : repository
1720 .with()
1721 .id(interpolator.interpolate(repository.getId(), callback))
1722 .url(interpolator.interpolate(repository.getUrl(), callback))
1723 .build();
1724 }
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736 List<Profile> merge(List<Profile> profiles, Map<String, String> userProperties) {
1737 List<Profile> result = null;
1738 for (int i = 0; i < profiles.size(); i++) {
1739 Profile profile = profiles.get(i);
1740 Map<String, String> props = merge(profile.getProperties(), userProperties);
1741 if (props != null) {
1742 Profile merged = profile.withProperties(props);
1743 if (result == null) {
1744 result = new ArrayList<>(profiles);
1745 }
1746 result.set(i, merged);
1747 }
1748 }
1749 return result != null ? result : profiles;
1750 }
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762 Map<String, String> merge(Map<String, String> properties, Map<String, String> userProperties) {
1763 Map<String, String> result = null;
1764 for (Map.Entry<String, String> entry : properties.entrySet()) {
1765 String key = entry.getKey();
1766 String value = userProperties.get(key);
1767 if (value != null && !Objects.equals(value, entry.getValue())) {
1768 if (result == null) {
1769 result = new LinkedHashMap<>(properties);
1770 }
1771 result.put(entry.getKey(), value);
1772 }
1773 }
1774 return result;
1775 }
1776
1777 Model readRawModel() throws ModelBuilderException {
1778
1779 readFileModel();
1780 Model model = cache(request.getSource(), RAW, this::doReadRawModel);
1781
1782 result.setRawModel(model);
1783 return model;
1784 }
1785
1786 private Model doReadRawModel() throws ModelBuilderException {
1787 Model rawModel = readFileModel();
1788
1789 if (!MODEL_VERSION_4_0_0.equals(rawModel.getModelVersion()) && isBuildRequest()) {
1790 rawModel = transformFileToRaw(rawModel);
1791 }
1792
1793 for (var transformer : transformers) {
1794 rawModel = transformer.transformRawModel(rawModel);
1795 }
1796
1797 modelValidator.validateRawModel(
1798 session,
1799 rawModel,
1800 isBuildRequest() ? ModelValidator.VALIDATION_LEVEL_STRICT : ModelValidator.VALIDATION_LEVEL_MINIMAL,
1801 this);
1802
1803 if (hasFatalErrors()) {
1804 throw newModelBuilderException();
1805 }
1806
1807 return rawModel;
1808 }
1809
1810
1811
1812
1813 private record ParentModelWithProfiles(Model model, List<Profile> activatedProfiles) {}
1814
1815
1816
1817
1818 Model readAsParentModel(DefaultProfileActivationContext profileActivationContext, Set<String> parentChain)
1819 throws ModelBuilderException {
1820 Map<DefaultProfileActivationContext.Record, ParentModelWithProfiles> parentsPerContext =
1821 cache(request.getSource(), PARENT, ConcurrentHashMap::new);
1822
1823 for (Map.Entry<DefaultProfileActivationContext.Record, ParentModelWithProfiles> e :
1824 parentsPerContext.entrySet()) {
1825 if (e.getKey().matches(profileActivationContext)) {
1826 ParentModelWithProfiles cached = e.getValue();
1827
1828
1829
1830
1831 if (profileActivationContext.record != null) {
1832 replayRecordIntoContext(e.getKey(), profileActivationContext);
1833 }
1834
1835 addActivePomProfiles(cached.activatedProfiles());
1836 return cached.model();
1837 }
1838 }
1839
1840
1841
1842
1843
1844
1845 DefaultProfileActivationContext ctx = profileActivationContext.start();
1846 ParentModelWithProfiles modelWithProfiles = doReadAsParentModel(ctx, parentChain);
1847 DefaultProfileActivationContext.Record record = ctx.stop();
1848 replayRecordIntoContext(record, profileActivationContext);
1849
1850 parentsPerContext.put(record, modelWithProfiles);
1851 addActivePomProfiles(modelWithProfiles.activatedProfiles());
1852 return modelWithProfiles.model();
1853 }
1854
1855 private ParentModelWithProfiles doReadAsParentModel(
1856 DefaultProfileActivationContext childProfileActivationContext, Set<String> parentChain)
1857 throws ModelBuilderException {
1858 Model raw = readRawModel();
1859 Model parentData = readParent(raw, raw.getParent(), childProfileActivationContext, parentChain);
1860 DefaultInheritanceAssembler defaultInheritanceAssembler =
1861 new DefaultInheritanceAssembler(new DefaultInheritanceAssembler.InheritanceModelMerger() {
1862 @Override
1863 protected void mergeModel_Modules(
1864 Model.Builder builder,
1865 Model target,
1866 Model source,
1867 boolean sourceDominant,
1868 Map<Object, Object> context) {}
1869
1870 @Override
1871 protected void mergeModel_Subprojects(
1872 Model.Builder builder,
1873 Model target,
1874 Model source,
1875 boolean sourceDominant,
1876 Map<Object, Object> context) {}
1877 });
1878 Model parent = defaultInheritanceAssembler.assembleModelInheritance(raw, parentData, request, this);
1879 for (Mixin mixin : parent.getMixins()) {
1880 Model parentModel = resolveParent(parent, mixin, childProfileActivationContext, parentChain);
1881 parent = defaultInheritanceAssembler.assembleModelInheritance(parent, parentModel, request, this);
1882 }
1883
1884
1885
1886
1887
1888
1889
1890
1891 List<Profile> parentActivePomProfiles =
1892 getActiveProfiles(parent.getProfiles(), childProfileActivationContext);
1893
1894
1895 Model injectedParentModel = profileInjector
1896 .injectProfiles(parent, parentActivePomProfiles, request, this)
1897 .withProfiles(List.of());
1898
1899
1900 return new ParentModelWithProfiles(injectedParentModel.withParent(null), parentActivePomProfiles);
1901 }
1902
1903 private Model importDependencyManagement(Model model, Collection<String> importIds) {
1904 DependencyManagement depMgmt = model.getDependencyManagement();
1905
1906 if (depMgmt == null) {
1907 return model;
1908 }
1909
1910 String importing = model.getGroupId() + ':' + model.getArtifactId() + ':' + model.getVersion();
1911
1912 importIds.add(importing);
1913
1914 List<DependencyManagement> importMgmts = null;
1915
1916 List<Dependency> deps = new ArrayList<>(depMgmt.getDependencies());
1917 for (Iterator<Dependency> it = deps.iterator(); it.hasNext(); ) {
1918 Dependency dependency = it.next();
1919
1920 if (!("pom".equals(dependency.getType()) && "import".equals(dependency.getScope()))
1921 || "bom".equals(dependency.getType())) {
1922 continue;
1923 }
1924
1925 it.remove();
1926
1927 DependencyManagement importMgmt = loadDependencyManagement(dependency, importIds);
1928
1929 if (importMgmt != null) {
1930 if (importMgmts == null) {
1931 importMgmts = new ArrayList<>();
1932 }
1933
1934 importMgmts.add(importMgmt);
1935 }
1936 }
1937
1938 importIds.remove(importing);
1939
1940 model = model.withDependencyManagement(
1941 model.getDependencyManagement().withDependencies(deps));
1942
1943 return dependencyManagementImporter.importManagement(model, importMgmts, request, this);
1944 }
1945
1946 private DependencyManagement loadDependencyManagement(Dependency dependency, Collection<String> importIds) {
1947 String groupId = dependency.getGroupId();
1948 String artifactId = dependency.getArtifactId();
1949 String version = dependency.getVersion();
1950
1951 if (groupId == null || groupId.isEmpty()) {
1952 add(
1953 Severity.ERROR,
1954 Version.BASE,
1955 "'dependencyManagement.dependencies.dependency.groupId' for " + dependency.getManagementKey()
1956 + " is missing.",
1957 dependency.getLocation(""));
1958 return null;
1959 }
1960 if (artifactId == null || artifactId.isEmpty()) {
1961 add(
1962 Severity.ERROR,
1963 Version.BASE,
1964 "'dependencyManagement.dependencies.dependency.artifactId' for " + dependency.getManagementKey()
1965 + " is missing.",
1966 dependency.getLocation(""));
1967 return null;
1968 }
1969 if (version == null || version.isEmpty()) {
1970 add(
1971 Severity.ERROR,
1972 Version.BASE,
1973 "'dependencyManagement.dependencies.dependency.version' for " + dependency.getManagementKey()
1974 + " is missing.",
1975 dependency.getLocation(""));
1976 return null;
1977 }
1978
1979 String imported = groupId + ':' + artifactId + ':' + version;
1980
1981 if (importIds.contains(imported)) {
1982 StringBuilder message =
1983 new StringBuilder("The dependencies of type=pom and with scope=import form a cycle: ");
1984 for (String modelId : importIds) {
1985 message.append(modelId).append(" -> ");
1986 }
1987 message.append(imported);
1988 add(Severity.ERROR, Version.BASE, message.toString());
1989 return null;
1990 }
1991
1992 Model importModel = cache(
1993 repositories,
1994 groupId,
1995 artifactId,
1996 version,
1997 null,
1998 IMPORT,
1999 () -> doLoadDependencyManagement(dependency, groupId, artifactId, version, importIds));
2000 DependencyManagement importMgmt = importModel != null ? importModel.getDependencyManagement() : null;
2001 if (importMgmt == null) {
2002 importMgmt = DependencyManagement.newInstance();
2003 }
2004
2005
2006 List<Exclusion> exclusions = dependency.getExclusions();
2007 if (importMgmt != null && !exclusions.isEmpty()) {
2008
2009 List<Dependency> dependencies = importMgmt.getDependencies().stream()
2010 .filter(candidate -> exclusions.stream().noneMatch(exclusion -> match(exclusion, candidate)))
2011 .map(candidate -> addExclusions(candidate, exclusions))
2012 .collect(Collectors.toList());
2013 importMgmt = importMgmt.withDependencies(dependencies);
2014 }
2015
2016 return importMgmt;
2017 }
2018
2019 @SuppressWarnings("checkstyle:parameternumber")
2020 private Model doLoadDependencyManagement(
2021 Dependency dependency,
2022 String groupId,
2023 String artifactId,
2024 String version,
2025 Collection<String> importIds) {
2026 Model importModel;
2027 ModelSource importSource;
2028 try {
2029 importSource = resolveReactorModel(groupId, artifactId, version);
2030 if (importSource == null) {
2031 importSource = modelResolver.resolveModel(
2032 request.getSession(), repositories, dependency, new AtomicReference<>());
2033 }
2034 } catch (ModelBuilderException | ModelResolverException e) {
2035 StringBuilder buffer = new StringBuilder(256);
2036 buffer.append("Non-resolvable import POM");
2037 if (!containsCoordinates(e.getMessage(), groupId, artifactId, version)) {
2038 buffer.append(' ').append(ModelProblemUtils.toId(groupId, artifactId, version));
2039 }
2040 buffer.append(": ").append(e.getMessage());
2041
2042 add(Severity.ERROR, Version.BASE, buffer.toString(), dependency.getLocation(""), e);
2043 return null;
2044 }
2045
2046 Path rootDirectory;
2047 try {
2048 rootDirectory = request.getSession().getRootDirectory();
2049 } catch (IllegalStateException e) {
2050 rootDirectory = null;
2051 }
2052 if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT && rootDirectory != null) {
2053 Path sourcePath = importSource.getPath();
2054 if (sourcePath != null && sourcePath.startsWith(rootDirectory)) {
2055 add(
2056 Severity.WARNING,
2057 Version.BASE,
2058 "BOM imports from within reactor should be avoided",
2059 dependency.getLocation(""));
2060 }
2061 }
2062
2063 final ModelBuilderResult importResult;
2064 try {
2065 ModelBuilderRequest importRequest = ModelBuilderRequest.builder()
2066 .session(request.getSession())
2067 .requestType(ModelBuilderRequest.RequestType.CONSUMER_DEPENDENCY)
2068 .systemProperties(request.getSystemProperties())
2069 .userProperties(request.getUserProperties())
2070 .source(importSource)
2071 .repositories(repositories)
2072 .build();
2073 ModelBuilderSessionState modelBuilderSession = derive(importRequest);
2074
2075 modelBuilderSession.buildEffectiveModel(importIds);
2076 importResult = modelBuilderSession.result;
2077 } catch (ModelBuilderException e) {
2078 return null;
2079 }
2080
2081 importModel = importResult.getEffectiveModel();
2082
2083 return importModel;
2084 }
2085
2086 ModelSource resolveReactorModel(String groupId, String artifactId, String version)
2087 throws ModelBuilderException {
2088 Set<ModelSource> sources = mappedSources.get(new GAKey(groupId, artifactId));
2089 if (sources != null) {
2090 for (ModelSource source : sources) {
2091 Model model = derive(source).readRawModel();
2092 if (Objects.equals(getVersion(model), version)) {
2093 return source;
2094 }
2095 }
2096
2097 }
2098 return null;
2099 }
2100
2101 private <T> T cache(
2102 List<RemoteRepository> repositories,
2103 String groupId,
2104 String artifactId,
2105 String version,
2106 String classifier,
2107 String tag,
2108 Supplier<T> supplier) {
2109 RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
2110 try {
2111 RgavCacheKey r = new RgavCacheKey(
2112 session, trace.mvnTrace(), repositories, groupId, artifactId, version, classifier, tag);
2113 SourceResponse<RgavCacheKey, T> response =
2114 InternalSession.from(session).request(r, tr -> new SourceResponse<>(tr, supplier.get()));
2115 return response.response;
2116 } finally {
2117 RequestTraceHelper.exit(trace);
2118 }
2119 }
2120
2121 private <T> T cache(Source source, String tag, Supplier<T> supplier) throws ModelBuilderException {
2122 RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
2123 try {
2124 SourceCacheKey r = new SourceCacheKey(session, trace.mvnTrace(), source, tag);
2125 SourceResponse<SourceCacheKey, T> response =
2126 InternalSession.from(session).request(r, tr -> new SourceResponse<>(tr, supplier.get()));
2127 return response.response;
2128 } finally {
2129 RequestTraceHelper.exit(trace);
2130 }
2131 }
2132
2133 boolean isBuildRequest() {
2134 return request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT
2135 || request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_EFFECTIVE
2136 || request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_CONSUMER;
2137 }
2138
2139 boolean isBuildRequestWithActivation() {
2140 return request.getRequestType() != ModelBuilderRequest.RequestType.BUILD_CONSUMER;
2141 }
2142
2143
2144
2145
2146
2147
2148 private void replayRecordIntoContext(
2149 DefaultProfileActivationContext.Record cachedRecord, DefaultProfileActivationContext targetContext) {
2150 if (targetContext.record == null) {
2151 return;
2152 }
2153
2154
2155
2156 DefaultProfileActivationContext.Record targetRecord = targetContext.record;
2157
2158
2159 cachedRecord.usedActiveProfiles.forEach(targetRecord.usedActiveProfiles::putIfAbsent);
2160
2161
2162 cachedRecord.usedInactiveProfiles.forEach(targetRecord.usedInactiveProfiles::putIfAbsent);
2163
2164
2165 cachedRecord.usedSystemProperties.forEach(targetRecord.usedSystemProperties::putIfAbsent);
2166
2167
2168 cachedRecord.usedUserProperties.forEach(targetRecord.usedUserProperties::putIfAbsent);
2169
2170
2171 cachedRecord.usedModelProperties.forEach(targetRecord.usedModelProperties::putIfAbsent);
2172
2173
2174 cachedRecord.usedModelInfos.forEach(targetRecord.usedModelInfos::putIfAbsent);
2175
2176
2177 cachedRecord.usedExists.forEach(targetRecord.usedExists::putIfAbsent);
2178 }
2179 }
2180
2181 @SuppressWarnings("deprecation")
2182 private static List<String> getSubprojects(Model activated) {
2183 List<String> subprojects = activated.getSubprojects();
2184 if (subprojects.isEmpty()) {
2185 subprojects = activated.getModules();
2186 }
2187 return subprojects;
2188 }
2189
2190
2191
2192
2193
2194
2195
2196
2197 @SuppressWarnings("deprecation")
2198 private static boolean hasSubprojectsDefined(Model model) {
2199
2200
2201 return model.getLocation("subprojects") != null || model.getLocation("modules") != null;
2202 }
2203
2204 @Override
2205 public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException {
2206 RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(request.getSession(), request);
2207 try {
2208 ModelBuilderSessionState build = new ModelBuilderSessionState(request);
2209 Model model = build.readRawModel();
2210 if (build.hasErrors()) {
2211 throw build.newModelBuilderException();
2212 }
2213 return model;
2214 } finally {
2215
2216 try {
2217 clearRequestScopedCache(request);
2218 } catch (Exception e) {
2219
2220 logger.debug("Failed to clear REQUEST_SCOPED cache for raw model request: {}", request, e);
2221 }
2222 RequestTraceHelper.exit(trace);
2223 }
2224 }
2225
2226 static String getGroupId(Model model) {
2227 String groupId = model.getGroupId();
2228 if (groupId == null && model.getParent() != null) {
2229 groupId = model.getParent().getGroupId();
2230 }
2231 return groupId;
2232 }
2233
2234 static String getVersion(Model model) {
2235 String version = model.getVersion();
2236 if (version == null && model.getParent() != null) {
2237 version = model.getParent().getVersion();
2238 }
2239 return version;
2240 }
2241
2242 private DefaultProfileActivationContext getProfileActivationContext(ModelBuilderRequest request, Model model) {
2243 return new DefaultProfileActivationContext(
2244 pathTranslator,
2245 rootLocator,
2246 interpolator,
2247 request.getActiveProfileIds(),
2248 request.getInactiveProfileIds(),
2249 request.getSystemProperties(),
2250 request.getUserProperties(),
2251 model);
2252 }
2253
2254 private Map<String, Activation> getProfileActivations(Model model) {
2255 return model.getProfiles().stream()
2256 .filter(p -> p.getActivation() != null)
2257 .collect(Collectors.toMap(Profile::getId, Profile::getActivation));
2258 }
2259
2260 private Model injectProfileActivations(Model model, Map<String, Activation> activations) {
2261 List<Profile> profiles = new ArrayList<>();
2262 boolean modified = false;
2263 for (Profile profile : model.getProfiles()) {
2264 Activation activation = profile.getActivation();
2265 if (activation != null) {
2266
2267 profile = profile.withActivation(activations.get(profile.getId()));
2268 modified = true;
2269 }
2270 profiles.add(profile);
2271 }
2272 return modified ? model.withProfiles(profiles) : model;
2273 }
2274
2275 private Model interpolateModel(Model model, ModelBuilderRequest request, ModelProblemCollector problems) {
2276 Model interpolatedModel =
2277 modelInterpolator.interpolateModel(model, model.getProjectDirectory(), request, problems);
2278 if (interpolatedModel.getParent() != null) {
2279 Map<String, String> map1 = request.getSession().getUserProperties();
2280 Map<String, String> map2 = model.getProperties();
2281 Map<String, String> map3 = request.getSession().getSystemProperties();
2282 UnaryOperator<String> cb = Interpolator.chain(map1::get, map2::get, map3::get);
2283 try {
2284 String interpolated =
2285 interpolator.interpolate(interpolatedModel.getParent().getVersion(), cb);
2286 interpolatedModel = interpolatedModel.withParent(
2287 interpolatedModel.getParent().withVersion(interpolated));
2288 } catch (Exception e) {
2289 problems.add(
2290 Severity.ERROR,
2291 Version.BASE,
2292 "Failed to interpolate field: "
2293 + interpolatedModel.getParent().getVersion()
2294 + " on class: ",
2295 e);
2296 }
2297 }
2298 interpolatedModel = interpolatedModel.withPomFile(model.getPomFile());
2299 return interpolatedModel;
2300 }
2301
2302 private boolean rawChildVersionReferencesParent(String rawChildModelVersion) {
2303 return rawChildModelVersion.equals("${pom.version}")
2304 || rawChildModelVersion.equals("${project.version}")
2305 || rawChildModelVersion.equals("${pom.parent.version}")
2306 || rawChildModelVersion.equals("${project.parent.version}");
2307 }
2308
2309 private Model getSuperModel(String modelVersion) {
2310 return superPomProvider.getSuperPom(modelVersion);
2311 }
2312
2313 private static org.apache.maven.api.model.Dependency addExclusions(
2314 org.apache.maven.api.model.Dependency candidate, List<Exclusion> exclusions) {
2315 return candidate.withExclusions(Stream.concat(candidate.getExclusions().stream(), exclusions.stream())
2316 .toList());
2317 }
2318
2319 private boolean match(Exclusion exclusion, Dependency candidate) {
2320 return match(exclusion.getGroupId(), candidate.getGroupId())
2321 && match(exclusion.getArtifactId(), candidate.getArtifactId());
2322 }
2323
2324 private boolean match(String match, String text) {
2325 return match.equals("*") || match.equals(text);
2326 }
2327
2328 private boolean containsCoordinates(String message, String groupId, String artifactId, String version) {
2329 return message != null
2330 && (groupId == null || message.contains(groupId))
2331 && (artifactId == null || message.contains(artifactId))
2332 && (version == null || message.contains(version));
2333 }
2334
2335 record GAKey(String groupId, String artifactId) {}
2336
2337 public record RgavCacheKey(
2338 Session session,
2339 RequestTrace trace,
2340 List<RemoteRepository> repositories,
2341 String groupId,
2342 String artifactId,
2343 String version,
2344 String classifier,
2345 String tag)
2346 implements Request<Session> {
2347 @Nonnull
2348 @Override
2349 public Session getSession() {
2350 return session;
2351 }
2352
2353 @Nullable
2354 @Override
2355 public RequestTrace getTrace() {
2356 return trace;
2357 }
2358
2359 @Override
2360 public boolean equals(Object o) {
2361 return o instanceof RgavCacheKey that
2362 && Objects.equals(tag, that.tag)
2363 && Objects.equals(groupId, that.groupId)
2364 && Objects.equals(version, that.version)
2365 && Objects.equals(artifactId, that.artifactId)
2366 && Objects.equals(classifier, that.classifier)
2367 && Objects.equals(repositories, that.repositories);
2368 }
2369
2370 @Override
2371 public int hashCode() {
2372 return Objects.hash(repositories, groupId, artifactId, version, classifier, tag);
2373 }
2374
2375 @Override
2376 public String toString() {
2377 StringBuilder sb = new StringBuilder();
2378 sb.append(getClass().getSimpleName()).append("[").append("gav='");
2379 if (groupId != null) {
2380 sb.append(groupId);
2381 }
2382 sb.append(":");
2383 if (artifactId != null) {
2384 sb.append(artifactId);
2385 }
2386 sb.append(":");
2387 if (version != null) {
2388 sb.append(version);
2389 }
2390 sb.append(":");
2391 if (classifier != null) {
2392 sb.append(classifier);
2393 }
2394 sb.append("', tag='");
2395 sb.append(tag);
2396 sb.append("']");
2397 return sb.toString();
2398 }
2399 }
2400
2401 public record SourceCacheKey(Session session, RequestTrace trace, Source source, String tag)
2402 implements Request<Session>, CacheMetadata {
2403 @Nonnull
2404 @Override
2405 public Session getSession() {
2406 return session;
2407 }
2408
2409 @Nullable
2410 @Override
2411 public RequestTrace getTrace() {
2412 return trace;
2413 }
2414
2415 @Override
2416 public CacheRetention getCacheRetention() {
2417 return source instanceof CacheMetadata cacheMetadata
2418 ? cacheMetadata.getCacheRetention()
2419 : CacheRetention.REQUEST_SCOPED;
2420 }
2421
2422 @Override
2423 public boolean equals(Object o) {
2424 return o instanceof SourceCacheKey that
2425 && Objects.equals(tag, that.tag)
2426 && Objects.equals(source, that.source);
2427 }
2428
2429 @Override
2430 public int hashCode() {
2431 return Objects.hash(source, tag);
2432 }
2433
2434 @Override
2435 public String toString() {
2436 return getClass().getSimpleName() + "[" + "location=" + source.getLocation() + ", tag=" + tag + ", path="
2437 + source.getPath() + ']';
2438 }
2439 }
2440
2441 public static class SourceResponse<R extends Request<?>, T> implements Result<R> {
2442 private final R request;
2443 private final T response;
2444
2445 SourceResponse(R request, T response) {
2446 this.request = request;
2447 this.response = response;
2448 }
2449
2450 @Nonnull
2451 @Override
2452 public R getRequest() {
2453 return request;
2454 }
2455 }
2456
2457 static class InterningTransformer implements XmlReaderRequest.Transformer {
2458 static final Set<String> DEFAULT_CONTEXTS = Set.of(
2459
2460 "groupId",
2461 "artifactId",
2462 "version",
2463 "namespaceUri",
2464 "packaging",
2465
2466
2467 "scope",
2468 "type",
2469 "classifier",
2470
2471
2472 "phase",
2473 "goal",
2474 "execution",
2475
2476
2477 "layout",
2478 "policy",
2479 "checksumPolicy",
2480 "updatePolicy",
2481
2482
2483 "modelVersion",
2484 "name",
2485 "url",
2486 "system",
2487 "distribution",
2488 "status",
2489
2490
2491 "connection",
2492 "developerConnection",
2493 "tag",
2494
2495
2496 "id",
2497 "inherited",
2498 "optional");
2499
2500 private final Set<String> contexts;
2501
2502
2503
2504
2505 InterningTransformer() {
2506 this.contexts = DEFAULT_CONTEXTS;
2507 }
2508
2509
2510
2511
2512
2513
2514 InterningTransformer(Session session) {
2515 this.contexts = parseContextsFromSession(session);
2516 }
2517
2518 private Set<String> parseContextsFromSession(Session session) {
2519 String contextsProperty = session.getUserProperties().get(Constants.MAVEN_MODEL_BUILDER_INTERNS);
2520 if (contextsProperty == null) {
2521 contextsProperty = session.getSystemProperties().get(Constants.MAVEN_MODEL_BUILDER_INTERNS);
2522 }
2523
2524 if (contextsProperty == null || contextsProperty.trim().isEmpty()) {
2525 return DEFAULT_CONTEXTS;
2526 }
2527
2528 return Arrays.stream(contextsProperty.split(","))
2529 .map(String::trim)
2530 .filter(s -> !s.isEmpty())
2531 .collect(Collectors.toSet());
2532 }
2533
2534 @Override
2535 public String transform(String input, String context) {
2536 return input != null && contexts.contains(context) ? input.intern() : input;
2537 }
2538
2539
2540
2541
2542
2543 Set<String> getContexts() {
2544 return contexts;
2545 }
2546 }
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556 private <REQ extends Request<?>> void clearRequestScopedCache(REQ req) {
2557 if (req.getSession() instanceof Session session) {
2558
2559 SessionData.Key<Cache> key = SessionData.key(Cache.class, CacheMetadata.class);
2560
2561
2562 Object outerRequestKey = getOuterRequest(req);
2563
2564 Cache<Object, Object> caches = session.getData().get(key);
2565 if (caches != null) {
2566 Object removedCache = caches.get(outerRequestKey);
2567 if (removedCache instanceof Cache<?, ?> map) {
2568 int beforeSize = map.size();
2569 map.removeIf((k, v) -> !(k instanceof RgavCacheKey) && !(k instanceof SourceCacheKey));
2570 int afterSize = map.size();
2571 if (logger.isDebugEnabled()) {
2572 logger.debug(
2573 "Cleared REQUEST_SCOPED cache for request: {}, removed {} entries, remaining entries: {}",
2574 outerRequestKey.getClass().getSimpleName(),
2575 afterSize - beforeSize,
2576 afterSize);
2577 }
2578 }
2579 }
2580 }
2581 }
2582
2583
2584
2585
2586
2587 private Object getOuterRequest(Request<?> req) {
2588 RequestTrace trace = req.getTrace();
2589 if (trace != null) {
2590 RequestTrace parent = trace.parent();
2591 if (parent != null && parent.data() instanceof Request<?> parentRequest) {
2592 return getOuterRequest(parentRequest);
2593 }
2594 }
2595 return req;
2596 }
2597
2598 private static <T, A> List<T> map(List<T> resources, BiFunction<T, A, T> mapper, A argument) {
2599 List<T> newResources = null;
2600 if (resources != null) {
2601 for (int i = 0; i < resources.size(); i++) {
2602 T resource = resources.get(i);
2603 T newResource = mapper.apply(resource, argument);
2604 if (newResource != resource) {
2605 if (newResources == null) {
2606 newResources = new ArrayList<>(resources);
2607 }
2608 newResources.set(i, newResource);
2609 }
2610 }
2611 }
2612 return newResources;
2613 }
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623 private static boolean isParentWithinRootDirectory(Path parentPath, Path rootDirectory) {
2624 if (parentPath == null || rootDirectory == null) {
2625 return true;
2626 }
2627
2628 try {
2629 Path normalizedParent = parentPath.toAbsolutePath().normalize();
2630 Path normalizedRoot = rootDirectory.toAbsolutePath().normalize();
2631
2632
2633 return normalizedParent.startsWith(normalizedRoot);
2634 } catch (Exception e) {
2635
2636 return true;
2637 }
2638 }
2639 }