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