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