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