[MNG-8294] Consistency checks when loading parent (#1784)

This commit is contained in:
Guillaume Nodet 2024-10-09 13:07:31 +02:00 committed by GitHub
parent 445236398d
commit fee69f2f89
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 296 additions and 264 deletions

View File

@ -52,6 +52,10 @@ public interface ModelBuilderRequest {
* The request is for building a model from a POM file in a project on the filesystem.
*/
BUILD_POM,
/**
* The request is for building the consumer POM.
*/
CONSUMER_POM,
/**
* The request is for building a model from a parent POM file from a downloaded artifact.
*/

View File

@ -1744,20 +1744,16 @@
<name>relativePath</name>
<version>4.0.0+</version>
<description>
The relative path of the parent {@code pom.xml} file within the checkout.
If not specified, it defaults to {@code ../pom.xml}.
The relative path of the parent subproject POM file or directory within the checkout.
If not specified, it defaults to {@code ..}, i.e. the parent directory.
Maven looks for the parent POM first in this location on
the filesystem, then the local repository, and lastly in the remote repo.
{@code relativePath} allows you to select a different location,
for example when your structure is flat, or deeper without an intermediate parent POM.
However, the group ID, artifact ID and version are still required,
and must match the file in the location given, or it will revert to the repository for the POM.
This feature is only for enhancing the development in a local checkout of that project.
Set the value to an empty string in case you want to disable the feature and always resolve
the parent POM from the repositories.
the filesystem if explicitly provided, then in the reactor if groupId and artifactId are provided,
then in the default parent directory, then the local repository, and lastly in the remote repo.
However, if the both relative path and the group ID / artifact ID are provided,
they must match the file in the location given.
Specify either the {@code relativePath} or the {@code groupId}/{@code artifactId}, not both.
</description>
<type>String</type>
<defaultValue>..</defaultValue>
</field>
</fields>
<codeSegments>

View File

@ -34,7 +34,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -829,6 +828,14 @@ public class DefaultModelBuilder implements ModelBuilder {
}
result.setEffectiveModel(resultModel);
// Set the default relative path for the parent in the file model
if (result.getFileModel().getParent() != null
&& result.getFileModel().getParent().getRelativePath() == null) {
result.setFileModel(result.getFileModel()
.withParent(result.getFileModel()
.getParent()
.withRelativePath(resultModel.getParent().getRelativePath())));
}
// effective model validation
modelValidator.validateEffectiveModel(
@ -849,10 +856,7 @@ public class DefaultModelBuilder implements ModelBuilder {
Parent parent = childModel.getParent();
if (parent != null) {
parentModel = readParentLocally(childModel);
if (parentModel == null) {
parentModel = resolveAndReadParentExternally(childModel);
}
parentModel = resolveParent(childModel);
if (!"pom".equals(parentModel.getPackaging())) {
add(
@ -877,43 +881,56 @@ public class DefaultModelBuilder implements ModelBuilder {
return parentModel;
}
private Model resolveParent(Model childModel) {
Model parentModel = null;
if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM
|| request.getRequestType() == ModelBuilderRequest.RequestType.CONSUMER_POM) {
parentModel = readParentLocally(childModel);
}
if (parentModel == null) {
parentModel = resolveAndReadParentExternally(childModel);
}
return parentModel;
}
private Model readParentLocally(Model childModel) throws ModelBuilderException {
ModelSource candidateSource = getParentPomFile(childModel, request.getSource());
ModelSource candidateSource = null;
Parent parent = childModel.getParent();
String parentPath = parent.getRelativePath();
if (parentPath != null && !parentPath.isEmpty()) {
candidateSource = request.getSource().resolve(modelProcessor::locateExistingPom, parentPath);
if (candidateSource == null) {
wrongParentRelativePath(childModel);
return null;
}
}
if (candidateSource == null) {
candidateSource = resolveReactorModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
}
if (candidateSource == null) {
candidateSource = request.getSource().resolve(modelProcessor::locateExistingPom, "..");
}
if (candidateSource == null) {
return null;
}
Model candidateModel = derive(candidateSource).readAsParentModel();
//
// TODO jvz Why isn't all this checking the job of the duty of the workspace resolver, we know that we
// have a model that is suitable, yet more checks are done here and the one for the version is problematic
// before because with parents as ranges it will never work in this scenario.
//
String groupId = getGroupId(candidateModel);
String artifactId = candidateModel.getArtifactId();
String version = getVersion(candidateModel);
Parent parent = childModel.getParent();
// Ensure that relative path and GA match, if both are provided
if (groupId == null
|| !groupId.equals(parent.getGroupId())
|| artifactId == null
|| !artifactId.equals(parent.getArtifactId())) {
StringBuilder buffer = new StringBuilder(256);
buffer.append("'parent.relativePath'");
if (childModel != getRootModel()) {
buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel));
}
buffer.append(" points at ").append(groupId).append(':').append(artifactId);
buffer.append(" instead of ").append(parent.getGroupId()).append(':');
buffer.append(parent.getArtifactId()).append(", please verify your project structure");
setSource(childModel);
add(Severity.WARNING, Version.BASE, buffer.toString(), parent.getLocation(""));
mismatchRelativePathAndGA(childModel, groupId, artifactId);
return null;
}
String version = getVersion(candidateModel);
if (version != null && parent.getVersion() != null && !version.equals(parent.getVersion())) {
try {
VersionRange parentRange = versionParser.parseVersionRange(parent.getVersion());
@ -946,15 +963,41 @@ public class DefaultModelBuilder implements ModelBuilder {
return null;
}
}
//
// Here we just need to know that a version is fine to use but this validation we can do in our workspace
// resolver.
//
return candidateModel;
}
private void mismatchRelativePathAndGA(Model childModel, String groupId, String artifactId) {
Parent parent = childModel.getParent();
StringBuilder buffer = new StringBuilder(256);
buffer.append("'parent.relativePath'");
if (childModel != getRootModel()) {
buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel));
}
buffer.append(" points at ").append(groupId).append(':').append(artifactId);
buffer.append(" instead of ").append(parent.getGroupId()).append(':');
buffer.append(parent.getArtifactId()).append(", please verify your project structure");
setSource(childModel);
boolean warn = MODEL_VERSION_4_0_0.equals(childModel.getModelVersion())
|| childModel.getParent().getRelativePath() == null;
add(warn ? Severity.WARNING : Severity.FATAL, Version.BASE, buffer.toString(), parent.getLocation(""));
}
private void wrongParentRelativePath(Model childModel) {
Parent parent = childModel.getParent();
String parentPath = parent.getRelativePath();
StringBuilder buffer = new StringBuilder(256);
buffer.append("'parent.relativePath'");
if (childModel != getRootModel()) {
buffer.append(" of POM ").append(ModelProblemUtils.toSourceHint(childModel));
}
buffer.append(" points at '").append(parentPath);
buffer.append("' but no POM could be found, please verify your project structure");
setSource(childModel);
add(Severity.FATAL, Version.BASE, buffer.toString(), parent.getLocation(""));
}
Model resolveAndReadParentExternally(Model childModel) throws ModelBuilderException {
ModelBuilderRequest request = this.request;
setSource(childModel);
@ -981,7 +1024,7 @@ public class DefaultModelBuilder implements ModelBuilder {
ModelSource modelSource;
try {
modelSource = resolveReactorModel(groupId, artifactId, version);
modelSource = resolveReactorModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
if (modelSource == null) {
AtomicReference<Parent> modified = new AtomicReference<>();
modelSource = modelResolver.resolveModel(request.getSession(), repositories, parent, modified);
@ -1000,13 +1043,8 @@ public class DefaultModelBuilder implements ModelBuilder {
buffer.append(" for ").append(ModelProblemUtils.toId(childModel));
}
buffer.append(": ").append(e.getMessage());
if (childModel.getProjectDirectory() != null) {
if (parent.getRelativePath() == null
|| parent.getRelativePath().isEmpty()) {
buffer.append(" and 'parent.relativePath' points at no local POM");
} else {
buffer.append(" and 'parent.relativePath' points at wrong local POM");
}
if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) {
buffer.append(" and parent could not be found in reactor");
}
add(Severity.FATAL, Version.BASE, buffer.toString(), parent.getLocation(""), e);
@ -1050,8 +1088,7 @@ public class DefaultModelBuilder implements ModelBuilder {
DefaultProfileActivationContext profileActivationContext = getProfileActivationContext(request, inputModel);
setSource("(external profiles)");
List<Profile> activeExternalProfiles =
profileSelector.getActiveProfiles(request.getProfiles(), profileActivationContext, this);
List<Profile> activeExternalProfiles = getActiveProfiles(request.getProfiles(), profileActivationContext);
result.setActiveExternalProfiles(activeExternalProfiles);
@ -1066,8 +1103,7 @@ public class DefaultModelBuilder implements ModelBuilder {
profileActivationContext.setProjectProperties(inputModel.getProperties());
setSource(inputModel);
List<Profile> activePomProfiles =
profileSelector.getActiveProfiles(inputModel.getProfiles(), profileActivationContext, this);
List<Profile> activePomProfiles = getActiveProfiles(inputModel.getProfiles(), profileActivationContext);
// model normalization
setSource(inputModel);
@ -1109,12 +1145,29 @@ public class DefaultModelBuilder implements ModelBuilder {
}
Model parentModel = readParent(inputModel);
// Now that we have read the parent, we can set the relative
// path correctly if it was not set in the input model
if (inputModel.getParent() != null && inputModel.getParent().getRelativePath() == null) {
String relPath;
if (parentModel.getPomFile() != null
&& (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM
|| request.getRequestType() == ModelBuilderRequest.RequestType.CONSUMER_POM)) {
relPath = inputModel
.getPomFile()
.getParent()
.relativize(parentModel.getPomFile().getParent())
.toString();
} else {
relPath = "..";
}
inputModel = inputModel.withParent(inputModel.getParent().withRelativePath(relPath));
}
List<Profile> parentInterpolatedProfiles =
interpolateActivations(parentModel.getProfiles(), profileActivationContext, this);
// profile injection
List<Profile> parentActivePomProfiles =
profileSelector.getActiveProfiles(parentInterpolatedProfiles, profileActivationContext, this);
getActiveProfiles(parentInterpolatedProfiles, profileActivationContext);
Model injectedParentModel = profileInjector
.injectProfiles(parentModel, parentActivePomProfiles, request, this)
.withProfiles(List.of());
@ -1131,8 +1184,7 @@ public class DefaultModelBuilder implements ModelBuilder {
interpolateActivations(model.getProfiles(), profileActivationContext, this);
// profile injection
List<Profile> activePomProfiles =
profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, this);
List<Profile> activePomProfiles = getActiveProfiles(interpolatedProfiles, profileActivationContext);
result.setActivePomProfiles(activePomProfiles);
model = profileInjector.injectProfiles(model, activePomProfiles, request, this);
model = profileInjector.injectProfiles(model, activeExternalProfiles, request, this);
@ -1160,6 +1212,15 @@ public class DefaultModelBuilder implements ModelBuilder {
return resultModel;
}
private List<Profile> getActiveProfiles(
Collection<Profile> interpolatedProfiles, DefaultProfileActivationContext profileActivationContext) {
if (request.getRequestType() != ModelBuilderRequest.RequestType.CONSUMER_POM) {
return profileSelector.getActiveProfiles(interpolatedProfiles, profileActivationContext, this);
} else {
return List.of();
}
}
Model readFileModel() throws ModelBuilderException {
Model model = cache(request.getSource(), FILE, this::doReadFileModel);
// set the file model in the result outside the cache
@ -1248,7 +1309,8 @@ public class DefaultModelBuilder implements ModelBuilder {
}
}
if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) {
if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM
|| request.getRequestType() == ModelBuilderRequest.RequestType.CONSUMER_POM) {
model = model.withPomFile(modelSource.getPath());
Parent parent = model.getParent();
@ -1256,10 +1318,11 @@ public class DefaultModelBuilder implements ModelBuilder {
String groupId = parent.getGroupId();
String artifactId = parent.getArtifactId();
String version = parent.getVersion();
String path = Optional.ofNullable(parent.getRelativePath()).orElse("..");
if (version == null && !path.isEmpty()) {
String path = parent.getRelativePath();
if ((groupId == null || artifactId == null || version == null)
&& (path == null || !path.isEmpty())) {
Path pomFile = model.getPomFile();
Path relativePath = Paths.get(path);
Path relativePath = Paths.get(path != null ? path : "..");
Path pomPath = pomFile.resolveSibling(relativePath).normalize();
if (Files.isDirectory(pomPath)) {
pomPath = modelProcessor.locateExistingPom(pomPath);
@ -1267,18 +1330,23 @@ public class DefaultModelBuilder implements ModelBuilder {
if (pomPath != null && Files.isRegularFile(pomPath)) {
Model parentModel =
derive(ModelSource.fromPath(pomPath)).readFileModel();
if (parentModel != null) {
String parentGroupId = getGroupId(parentModel);
String parentArtifactId = parentModel.getArtifactId();
String parentVersion = getVersion(parentModel);
if ((groupId == null || groupId.equals(parentGroupId))
&& (artifactId == null || artifactId.equals(parentArtifactId))) {
model = model.withParent(parent.with()
.groupId(parentGroupId)
.artifactId(parentArtifactId)
.version(parentVersion)
.build());
}
String parentGroupId = getGroupId(parentModel);
String parentArtifactId = parentModel.getArtifactId();
String parentVersion = getVersion(parentModel);
if ((groupId == null || groupId.equals(parentGroupId))
&& (artifactId == null || artifactId.equals(parentArtifactId))
&& (version == null || version.equals(parentVersion))) {
model = model.withParent(parent.with()
.groupId(parentGroupId)
.artifactId(parentArtifactId)
.version(parentVersion)
.build());
} else {
mismatchRelativePathAndGA(model, parentGroupId, parentArtifactId);
}
} else {
if (!MODEL_VERSION_4_0_0.equals(model.getModelVersion()) && path != null) {
wrongParentRelativePath(model);
}
}
}
@ -1369,7 +1437,8 @@ public class DefaultModelBuilder implements ModelBuilder {
Model rawModel = readFileModel();
if (!MODEL_VERSION_4_0_0.equals(rawModel.getModelVersion())
&& request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM) {
&& (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM
|| request.getRequestType() == ModelBuilderRequest.RequestType.CONSUMER_POM)) {
rawModel = transformFileToRaw(rawModel);
}
@ -1600,7 +1669,7 @@ public class DefaultModelBuilder implements ModelBuilder {
.source(importSource)
.repositories(repositories)
.build();
DefaultModelBuilderSession modelBuilderSession = new DefaultModelBuilderSession(importRequest);
DefaultModelBuilderSession modelBuilderSession = derive(importRequest);
// build the effective model
modelBuilderSession.buildEffectiveModel(importIds);
importResult = modelBuilderSession.result;
@ -1812,15 +1881,6 @@ public class DefaultModelBuilder implements ModelBuilder {
|| rawChildModelVersion.equals("${project.parent.version}");
}
private ModelSource getParentPomFile(Model childModel, ModelSource source) {
String parentPath = childModel.getParent().getRelativePath();
if (parentPath == null || parentPath.isEmpty()) {
return null;
} else {
return source.resolve(modelProcessor::locateExistingPom, parentPath);
}
}
private Model getSuperModel(String modelVersion) {
return superPomProvider.getSuperPom(modelVersion);
}

View File

@ -326,6 +326,24 @@ public class DefaultModelValidator implements ModelValidator {
"is either LATEST or RELEASE (both of them are being deprecated)",
parent);
}
if (parent.getRelativePath() != null
&& !parent.getRelativePath().isEmpty()
&& (parent.getGroupId() != null && !parent.getGroupId().isEmpty()
|| parent.getArtifactId() != null
&& !parent.getArtifactId().isEmpty())
&& validationLevel >= ModelValidator.VALIDATION_LEVEL_MAVEN_4_0
&& VALID_MODEL_VERSIONS.contains(m.getModelVersion())
&& !Objects.equals(m.getModelVersion(), ModelBuilder.MODEL_VERSION_4_0_0)) {
addViolation(
problems,
Severity.WARNING,
Version.BASE,
"parent.relativePath",
null,
"only specify relativePath or groupId/artifactId in modelVersion 4.1.0",
parent);
}
}
if (validationLevel == ModelValidator.VALIDATION_LEVEL_MINIMAL) {

View File

@ -21,10 +21,14 @@ package org.apache.maven.internal.transformation.impl;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.maven.api.SessionData;
@ -35,42 +39,17 @@ import org.apache.maven.api.model.Model;
import org.apache.maven.api.model.ModelBase;
import org.apache.maven.api.model.Profile;
import org.apache.maven.api.model.Repository;
import org.apache.maven.api.services.Interpolator;
import org.apache.maven.api.services.ModelBuilder;
import org.apache.maven.api.services.ModelBuilderException;
import org.apache.maven.api.services.ModelBuilderRequest;
import org.apache.maven.api.services.ModelBuilderResult;
import org.apache.maven.api.services.ModelProblemCollector;
import org.apache.maven.api.services.ModelSource;
import org.apache.maven.api.services.SuperPomProvider;
import org.apache.maven.api.services.model.DependencyManagementImporter;
import org.apache.maven.api.services.model.DependencyManagementInjector;
import org.apache.maven.api.services.model.InheritanceAssembler;
import org.apache.maven.api.services.Source;
import org.apache.maven.api.services.model.LifecycleBindingsInjector;
import org.apache.maven.api.services.model.ModelCacheFactory;
import org.apache.maven.api.services.model.ModelInterpolator;
import org.apache.maven.api.services.model.ModelNormalizer;
import org.apache.maven.api.services.model.ModelPathTranslator;
import org.apache.maven.api.services.model.ModelProcessor;
import org.apache.maven.api.services.model.ModelResolver;
import org.apache.maven.api.services.model.ModelUrlNormalizer;
import org.apache.maven.api.services.model.ModelValidator;
import org.apache.maven.api.services.model.ModelVersionParser;
import org.apache.maven.api.services.model.PluginConfigurationExpander;
import org.apache.maven.api.services.model.PluginManagementInjector;
import org.apache.maven.api.services.model.ProfileActivationContext;
import org.apache.maven.api.services.model.ProfileInjector;
import org.apache.maven.api.services.model.ProfileSelector;
import org.apache.maven.api.spi.ModelTransformer;
import org.apache.maven.internal.impl.InternalSession;
import org.apache.maven.internal.impl.model.DefaultModelBuilder;
import org.apache.maven.internal.impl.model.DefaultProfileSelector;
import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
import org.apache.maven.model.v4.MavenModelVersion;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystemSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Named
class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
@ -78,74 +57,14 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
public static final String POM_PACKAGING = "pom";
private final ProfileInjector profileInjector;
private final InheritanceAssembler inheritanceAssembler;
private final DependencyManagementImporter dependencyManagementImporter;
private final DependencyManagementInjector dependencyManagementInjector;
private final LifecycleBindingsInjector lifecycleBindingsInjector;
private final ModelInterpolator modelInterpolator;
private final ModelNormalizer modelNormalizer;
private final ModelPathTranslator modelPathTranslator;
private final ModelProcessor modelProcessor;
private final ModelUrlNormalizer modelUrlNormalizer;
private final ModelValidator modelValidator;
private final PluginConfigurationExpander pluginConfigurationExpander;
private final PluginManagementInjector pluginManagementInjector;
private final SuperPomProvider superPomProvider;
private final ModelVersionParser versionParser;
private final ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator;
private final List<ModelTransformer> transformers;
private final ModelCacheFactory modelCacheFactory;
private final ModelResolver modelResolver;
private final Interpolator interpolator;
@Inject
@SuppressWarnings("checkstyle:ParameterNumber")
DefaultConsumerPomBuilder(
ProfileInjector profileInjector,
InheritanceAssembler inheritanceAssembler,
DependencyManagementImporter dependencyManagementImporter,
DependencyManagementInjector dependencyManagementInjector,
LifecycleBindingsInjector lifecycleBindingsInjector,
ModelInterpolator modelInterpolator,
ModelNormalizer modelNormalizer,
ModelPathTranslator modelPathTranslator,
ModelProcessor modelProcessor,
ModelUrlNormalizer modelUrlNormalizer,
ModelValidator modelValidator,
PluginConfigurationExpander pluginConfigurationExpander,
PluginManagementInjector pluginManagementInjector,
SuperPomProvider superPomProvider,
ModelVersionParser versionParser,
ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator,
List<ModelTransformer> transformers,
ModelCacheFactory modelCacheFactory,
ModelResolver modelResolver,
Interpolator interpolator) {
this.profileInjector = profileInjector;
this.inheritanceAssembler = inheritanceAssembler;
this.dependencyManagementImporter = dependencyManagementImporter;
this.dependencyManagementInjector = dependencyManagementInjector;
DefaultConsumerPomBuilder(LifecycleBindingsInjector lifecycleBindingsInjector) {
this.lifecycleBindingsInjector = lifecycleBindingsInjector;
this.modelInterpolator = modelInterpolator;
this.modelNormalizer = modelNormalizer;
this.modelPathTranslator = modelPathTranslator;
this.modelProcessor = modelProcessor;
this.modelUrlNormalizer = modelUrlNormalizer;
this.modelValidator = modelValidator;
this.pluginConfigurationExpander = pluginConfigurationExpander;
this.pluginManagementInjector = pluginManagementInjector;
this.superPomProvider = superPomProvider;
this.versionParser = versionParser;
this.profileActivationFilePathInterpolator = profileActivationFilePathInterpolator;
this.transformers = transformers;
this.modelCacheFactory = modelCacheFactory;
this.modelResolver = modelResolver;
this.interpolator = interpolator;
}
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public Model build(RepositorySystemSession session, MavenProject project, Path src) throws ModelBuilderException {
Model model = project.getModel().getDelegate();
@ -174,49 +93,18 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
private ModelBuilderResult buildModel(RepositorySystemSession session, MavenProject project, Path src)
throws ModelBuilderException {
ProfileSelector customSelector = new DefaultProfileSelector() {
@Override
public List<Profile> getActiveProfiles(
Collection<Profile> profiles, ProfileActivationContext context, ModelProblemCollector problems) {
return new ArrayList<>();
}
};
// TODO: the custom selector should be used as a flag on the request
DefaultModelBuilder modelBuilder = new DefaultModelBuilder(
modelProcessor,
modelValidator,
modelNormalizer,
modelInterpolator,
modelPathTranslator,
modelUrlNormalizer,
superPomProvider,
inheritanceAssembler,
customSelector,
profileInjector,
pluginManagementInjector,
dependencyManagementInjector,
dependencyManagementImporter,
pluginConfigurationExpander,
profileActivationFilePathInterpolator,
versionParser,
transformers,
modelCacheFactory,
modelResolver,
interpolator);
InternalSession iSession = InternalSession.from(session);
ModelBuilderRequest.ModelBuilderRequestBuilder request = ModelBuilderRequest.builder();
request.requestType(ModelBuilderRequest.RequestType.BUILD_POM);
request.requestType(ModelBuilderRequest.RequestType.CONSUMER_POM);
request.session(iSession);
request.source(ModelSource.fromPath(src));
// in order to resolve parents, we need to fake being at the correct location
request.source(new PomConsumerModelSource(project.getModel().getPomPath(), src));
request.locationTracking(false);
request.systemProperties(session.getSystemProperties());
request.userProperties(session.getUserProperties());
request.lifecycleBindingsInjector(lifecycleBindingsInjector::injectLifecycleBindings);
ModelBuilder.ModelBuilderSession mbSession =
iSession.getData().get(SessionData.key(ModelBuilder.ModelBuilderSession.class));
if (mbSession == null) {
mbSession = modelBuilder.newSession();
}
return mbSession.build(request.build());
}
@ -320,4 +208,63 @@ class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
.filter(r -> !org.apache.maven.api.Repository.CENTRAL_ID.equals(r.getId()))
.collect(Collectors.toList());
}
static class PomConsumerModelSource implements ModelSource {
final Path path;
final Path src;
PomConsumerModelSource(Path path, Path src) {
this.path = path;
this.src = src;
}
@Override
public Path getPath() {
return path;
}
@Override
public InputStream openStream() throws IOException {
return Files.newInputStream(src);
}
@Override
public String getLocation() {
return src.toString();
}
@Override
public Source resolve(String relative) {
return ModelSource.fromPath(path.resolve(relative));
}
@Override
public ModelSource resolve(ModelLocator locator, String relative) {
String norm = relative.replace('\\', File.separatorChar).replace('/', File.separatorChar);
Path path = getPath().getParent().resolve(norm);
Path relatedPom = locator.locateExistingPom(path);
if (relatedPom != null) {
return ModelSource.fromPath(relatedPom);
}
return null;
}
@Override
public boolean equals(Object o) {
return this == o
|| o.getClass() == getClass()
&& Objects.equals(path, ((PomConsumerModelSource) o).path)
&& Objects.equals(src, ((PomConsumerModelSource) o).src);
}
@Override
public int hashCode() {
return Objects.hash(path, src);
}
@Override
public String toString() {
return "PomConsumerModelSource[" + "path=" + path + ']';
}
}
}

View File

@ -74,6 +74,7 @@ import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.bridge.MavenRepositorySystem;
import org.apache.maven.internal.impl.InternalSession;
import org.apache.maven.internal.impl.resolver.ArtifactDescriptorUtils;
import org.apache.maven.model.building.DefaultModelProblem;
import org.apache.maven.model.building.FileModelSource;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.model.building.ModelSource2;
@ -499,6 +500,11 @@ public class DefaultProjectBuilder implements ProjectBuilder {
List<ProjectBuildingResult> results = new ArrayList<>();
List<ModelBuilderResult> allModels = results(result).toList();
for (ModelBuilderResult r : allModels) {
List<ModelProblem> problems = new ArrayList<>(r.getProblems());
results(r)
.filter(c -> c != r)
.flatMap(c -> c.getProblems().stream())
.forEach(problems::remove);
if (r.getEffectiveModel() != null) {
File pom = r.getSource().getPath().toFile();
MavenProject project =
@ -510,7 +516,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
project.setExecutionRoot(pom.equals(pomFile));
initProject(project, r);
project.setCollectedProjects(results(r)
.filter(cr -> cr != r)
.filter(cr -> cr != r && cr.getEffectiveModel() != null)
.map(cr -> projectIndex.get(cr.getEffectiveModel().getId()))
.collect(Collectors.toList()));
@ -518,10 +524,9 @@ public class DefaultProjectBuilder implements ProjectBuilder {
if (request.isResolveDependencies()) {
resolutionResult = resolveDependencies(project);
}
results.add(
new DefaultProjectBuildingResult(project, convert(result.getProblems()), resolutionResult));
results.add(new DefaultProjectBuildingResult(project, convert(problems), resolutionResult));
} else {
results.add(new DefaultProjectBuildingResult(null, convert(result.getProblems()), null));
results.add(new DefaultProjectBuildingResult(null, convert(problems), null));
}
}
return results;
@ -535,20 +540,21 @@ public class DefaultProjectBuilder implements ProjectBuilder {
if (problems == null) {
return null;
}
return problems.stream()
.map(p -> (org.apache.maven.model.building.ModelProblem)
new org.apache.maven.model.building.DefaultModelProblem(
p.getMessage(),
org.apache.maven.model.building.ModelProblem.Severity.valueOf(
p.getSeverity().name()),
org.apache.maven.model.building.ModelProblem.Version.valueOf(
p.getVersion().name()),
p.getSource(),
p.getLineNumber(),
p.getColumnNumber(),
p.getModelId(),
p.getException()))
.toList();
return problems.stream().map(p -> convert(p)).toList();
}
private static org.apache.maven.model.building.ModelProblem convert(ModelProblem p) {
return new DefaultModelProblem(
p.getMessage(),
org.apache.maven.model.building.ModelProblem.Severity.valueOf(
p.getSeverity().name()),
org.apache.maven.model.building.ModelProblem.Version.valueOf(
p.getVersion().name()),
p.getSource(),
p.getLineNumber(),
p.getColumnNumber(),
p.getModelId(),
p.getException());
}
@SuppressWarnings({"checkstyle:methodlength", "deprecation"})

View File

@ -20,9 +20,6 @@ package org.apache.maven.internal.transformation.impl;
import javax.inject.Inject;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
@ -35,17 +32,17 @@ import org.apache.maven.api.SessionData;
import org.apache.maven.api.model.Model;
import org.apache.maven.api.model.Parent;
import org.apache.maven.api.services.ModelBuilder;
import org.apache.maven.api.services.ModelBuilderRequest;
import org.apache.maven.api.services.ModelSource;
import org.apache.maven.api.services.model.ModelResolver;
import org.apache.maven.api.services.model.ModelResolverException;
import org.apache.maven.api.spi.ModelTransformer;
import org.apache.maven.api.spi.ModelTransformerException;
import org.apache.maven.di.Injector;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.internal.impl.InternalMavenSession;
import org.apache.maven.internal.impl.InternalSession;
import org.apache.maven.internal.impl.model.DefaultModelBuilder;
import org.apache.maven.internal.transformation.AbstractRepositoryTestCase;
import org.apache.maven.model.v4.MavenStaxReader;
import org.apache.maven.project.MavenProject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -58,6 +55,9 @@ public class ConsumerPomBuilderTest extends AbstractRepositoryTestCase {
@Inject
ConsumerPomBuilder builder;
@Inject
ModelBuilder modelBuilder;
@BeforeEach
void setupTransformerContext() throws Exception {
// We need to hack things a bit here to get the transformer context to work
@ -71,33 +71,30 @@ public class ConsumerPomBuilderTest extends AbstractRepositoryTestCase {
// to maven central
getContainer().lookup(Injector.class).bindImplicit(MyModelResolver.class);
InternalSession iSession = InternalSession.from(session);
// set up the transformers
List<ModelTransformer> transformers = List.of(new CIFriendlyVersionModelTransformer(iSession));
Field transformersField = DefaultModelBuilder.class.getDeclaredField("transformers");
transformersField.setAccessible(true);
DefaultModelBuilder modelBuilder = (DefaultModelBuilder) getContainer().lookup(ModelBuilder.class);
transformersField.set(modelBuilder, transformers);
transformersField = DefaultConsumerPomBuilder.class.getDeclaredField("transformers");
transformersField.setAccessible(true);
transformersField.set(builder, transformers);
// set up the model resolver
iSession.getData().set(SessionData.key(ModelResolver.class), new MyModelResolver());
}
@Test
void testTrivialConsumer() throws Exception {
MavenProject project;
Path file = Paths.get("src/test/resources/consumer/trivial/child/pom.xml");
try (InputStream inputStream = Files.newInputStream(file)) {
org.apache.maven.model.Model model =
new org.apache.maven.model.Model(new MavenStaxReader().read(inputStream));
project = new MavenProject(model);
project.setOriginalModel(model);
}
InternalMavenSession.from(InternalSession.from(session))
.getMavenSession()
.getRequest()
.setRootDirectory(Paths.get("src/test/resources/consumer/trivial"));
Path file = Paths.get("src/test/resources/consumer/trivial/child/pom.xml");
ModelBuilder.ModelBuilderSession mbs = modelBuilder.newSession();
InternalSession.from(session).getData().set(SessionData.key(ModelBuilder.ModelBuilderSession.class), mbs);
Model orgModel = mbs.build(ModelBuilderRequest.builder()
.session(InternalSession.from(session))
.source(ModelSource.fromPath(file))
.requestType(ModelBuilderRequest.RequestType.BUILD_POM)
.build())
.getEffectiveModel();
MavenProject project = new MavenProject(orgModel);
project.setOriginalModel(new org.apache.maven.model.Model(orgModel));
Model model = builder.build(session, project, file);
assertNotNull(model);
@ -105,24 +102,26 @@ public class ConsumerPomBuilderTest extends AbstractRepositoryTestCase {
@Test
void testSimpleConsumer() throws Exception {
MavenProject project;
MavenExecutionRequest request = InternalMavenSession.from(InternalSession.from(session))
.getMavenSession()
.getRequest();
request.setRootDirectory(Paths.get("src/test/resources/consumer/simple"));
request.getUserProperties().setProperty("changelist", "MNG6957");
Path file = Paths.get("src/test/resources/consumer/simple/simple-parent/simple-weather/pom.xml");
InternalMavenSession.from(InternalSession.from(session))
.getMavenSession()
.getRequest()
.getUserProperties()
.setProperty("changelist", "MNG6957");
try (InputStream inputStream = Files.newInputStream(file)) {
org.apache.maven.model.Model model =
new org.apache.maven.model.Model(new MavenStaxReader().read(inputStream));
project = new MavenProject(model);
project.setOriginalModel(model);
}
InternalMavenSession.from(InternalSession.from(session))
.getMavenSession()
.getRequest()
.setRootDirectory(Paths.get("src/test/resources/consumer/simple"));
ModelBuilder.ModelBuilderSession mbs = modelBuilder.newSession();
InternalSession.from(session).getData().set(SessionData.key(ModelBuilder.ModelBuilderSession.class), mbs);
Model orgModel = mbs.build(ModelBuilderRequest.builder()
.session(InternalSession.from(session))
.source(ModelSource.fromPath(file))
.requestType(ModelBuilderRequest.RequestType.BUILD_POM)
.build())
.getEffectiveModel();
MavenProject project = new MavenProject(orgModel);
project.setOriginalModel(new org.apache.maven.model.Model(orgModel));
request.setRootDirectory(Paths.get("src/test/resources/consumer/simple"));
Model model = builder.build(session, project, file);
assertNotNull(model);

View File

@ -1616,8 +1616,10 @@ public class DefaultModelBuilder implements ModelBuilder {
}
String parentPath = childModel.getParent().getRelativePath();
if (parentPath == null || parentPath.isEmpty()) {
if (parentPath == null) {
parentPath = "..";
childModel.getParent().setRelativePath(parentPath);
} else if (parentPath.isEmpty()) {
return null;
}