Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -506,13 +506,16 @@ void performFor(String value, String locationKey, Consumer<String> mutator) {
/**
* Determines whether the given request builds a model to resolve a dependency, i.e. a POM
* read from a remote repository (a dependency POM, one of its parents, or an imported BOM)
* rather than a POM belonging to the project being built. Such requests use
* rather than a POM belonging to the project being built. Such requests have no POM file
* (see {@link ModelBuildingRequest#setPomFile(File)}) and use
* {@link ModelBuildingRequest#VALIDATION_LEVEL_MINIMAL}, see for instance
* {@code DefaultArtifactDescriptorReader#loadPom}; a project build uses
* {@link ModelBuildingRequest#VALIDATION_LEVEL_MAVEN_2_0} or higher.
* {@code DefaultArtifactDescriptorReader#loadPom}. A request with a POM file is in project
* mode whatever its validation level, so an embedder that builds its own project at
* {@code VALIDATION_LEVEL_MINIMAL} keeps full profile activation.
*/
private static boolean isExternalModelBuildingRequest(ModelBuildingRequest request) {
return request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
return request.getPomFile() == null
&& request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,13 @@ public Object getValue(String expression) {

valueSources.add(modelValueSource1);

// Models built at VALIDATION_LEVEL_MINIMAL are the models Maven builds while resolving
// dependency, parent and BOM-import POMs from a repository, not the operator's own
// project. Such models interpolate only against their own properties and a small set
// of environment-independent expressions; everything else in the user/system property
// space stays uninterpolated. Operator project builds use a higher validation level and
// keep the full set of value sources, unchanged from previous behavior.
// Models built without a POM file at VALIDATION_LEVEL_MINIMAL are the models Maven builds
// while resolving dependency, parent and BOM-import POMs from a repository, not the
// operator's own project. Such models interpolate only against their own properties and a
// small set of environment-independent expressions; everything else in the user/system
// property space stays uninterpolated. Operator project builds have a POM file or use a
// higher validation level and keep the full set of value sources, unchanged from previous
// behavior.
boolean restricted = restrictExternalModelInterpolation(config);

ValueSource userPropertiesValueSource = new MapBasedValueSource(config.getUserProperties());
Expand Down Expand Up @@ -234,7 +235,8 @@ public Object getValue(String expression) {
}

private static boolean restrictExternalModelInterpolation(ModelBuildingRequest config) {
return config.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0
return config.getPomFile() == null
&& config.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0
&& !Boolean.parseBoolean(config.getSystemProperties().getProperty(FULL_EXTERNAL_INTERPOLATION_PROPERTY))
&& !Boolean.parseBoolean(config.getUserProperties().getProperty(FULL_EXTERNAL_INTERPOLATION_PROPERTY));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@
*/
package org.apache.maven.model.building;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;

import org.apache.maven.model.Model;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
Expand Down Expand Up @@ -166,6 +171,44 @@ private Model build(int validationLevel, Properties systemProperties, Properties
return builder.build(request).getEffectiveModel();
}

private Model buildProjectAtMinimal(File pomFile, Properties systemProperties, Properties userProperties)
throws Exception {
ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();

DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
request.setPomFile(pomFile);
request.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
request.setSystemProperties(systemProperties);
request.setUserProperties(userProperties);
request.setModelResolver(new DefaultModelBuilderTest.BaseModelResolver());

return builder.build(request).getEffectiveModel();
}

private Model buildFromRepositoryAtMinimal(File pomFile, Properties systemProperties, Properties userProperties)
throws Exception {
ModelBuilder builder = new DefaultModelBuilderFactory().newInstance();

DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
request.setModelSource(new FileModelSource(pomFile));
request.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
request.setSystemProperties(systemProperties);
request.setUserProperties(userProperties);
request.setModelResolver(new DefaultModelBuilderTest.BaseModelResolver());

return builder.build(request).getEffectiveModel();
}

private File writePom(Path dir) throws Exception {
String pom = POM.replace(
"<pom.declared.prop>hello</pom.declared.prop>",
"<pom.declared.prop>hello</pom.declared.prop>\n"
+ " <interpolated.user.property>${user.gating.prop}</interpolated.user.property>");
Path file = dir.resolve("pom.xml");
Files.write(file, pom.getBytes(StandardCharsets.UTF_8));
return file.toFile();
}

private Properties systemPropertiesWithTestValues() {
Properties sp = new Properties();
sp.putAll(System.getProperties());
Expand Down Expand Up @@ -213,6 +256,42 @@ void testProjectBuildEvaluatesAllActivators() throws Exception {
"profile repository must be present in project build");
}

@Test
void testProjectModeAtMinimalValidationEvaluatesUserPropertyAndFileActivators(@TempDir Path dir) throws Exception {
// An embedder (an IDE) builds the operator's own project from its POM file at
// VALIDATION_LEVEL_MINIMAL for leniency. A POM file means project mode, so the
// repository-model sandbox must not apply.
Model model =
buildProjectAtMinimal(writePom(dir), systemPropertiesWithTestValues(), userPropertiesWithTestValues());

assertEquals(
"activated",
model.getProperties().get("profile.user.property"),
"user-property profile must fire for a project built from its POM file");
assertEquals(
"activated",
model.getProperties().get("profile.file"),
"file profile must fire for a project built from its POM file");
assertEquals(
"true",
model.getProperties().get("interpolated.user.property"),
"user properties must be interpolated for a project built from its POM file");
}

@Test
void testSamePomFromRepositoryAtMinimalValidationStaysSandboxed(@TempDir Path dir) throws Exception {
// The same POM loaded as a repository model (model source, no POM file) keeps the sandbox.
Model model = buildFromRepositoryAtMinimal(
writePom(dir), systemPropertiesWithTestValues(), userPropertiesWithTestValues());

assertNull(model.getProperties().get("profile.user.property"), "user-property profile must NOT fire");
assertNull(model.getProperties().get("profile.file"), "file profile must NOT fire");
assertEquals(
"${user.gating.prop}",
model.getProperties().get("interpolated.user.property"),
"user properties must stay literal in a repository model");
}

@Test
void testExternalModelFileActivationIsSuppressed() throws Exception {
// File activation must be suppressed in external model builds regardless of whether the
Expand Down
Loading