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