Skip to content

Commit 45415e1

Browse files
committed
validate all profile activation config strings
1 parent f4d7bac commit 45415e1

File tree

4 files changed

+200
-30
lines changed

4 files changed

+200
-30
lines changed

maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,24 @@
2424

2525
import java.io.File;
2626
import java.util.Arrays;
27+
import java.util.Deque;
2728
import java.util.HashMap;
2829
import java.util.HashSet;
30+
import java.util.Iterator;
31+
import java.util.LinkedList;
2932
import java.util.List;
3033
import java.util.Map;
3134
import java.util.Objects;
35+
import java.util.Optional;
3236
import java.util.Set;
37+
import java.util.function.Consumer;
38+
import java.util.function.Supplier;
3339
import java.util.regex.Matcher;
3440
import java.util.regex.Pattern;
41+
import java.util.stream.Collectors;
42+
import java.util.stream.StreamSupport;
3543

3644
import org.apache.maven.model.Activation;
37-
import org.apache.maven.model.ActivationFile;
3845
import org.apache.maven.model.Build;
3946
import org.apache.maven.model.BuildBase;
4047
import org.apache.maven.model.Dependency;
@@ -236,42 +243,97 @@ public void validateRawModel(Model m, ModelBuildingRequest request, ModelProblem
236243
}
237244

238245
private void validate30RawProfileActivation(ModelProblemCollector problems, Activation activation, String prefix) {
239-
if (activation == null || activation.getFile() == null) {
246+
if (activation == null) {
240247
return;
241248
}
249+
class ActivationFrame {
250+
String location;
251+
Optional<? extends InputLocationTracker> parent;
242252

243-
ActivationFile file = activation.getFile();
244-
245-
String path;
246-
String location;
247-
248-
if (file.getExists() != null && !file.getExists().isEmpty()) {
249-
path = file.getExists();
250-
location = "exists";
251-
} else if (file.getMissing() != null && !file.getMissing().isEmpty()) {
252-
path = file.getMissing();
253-
location = "missing";
254-
} else {
255-
return;
253+
ActivationFrame(String location, Optional<? extends InputLocationTracker> parent) {
254+
this.location = location;
255+
this.parent = parent;
256+
}
256257
}
257-
258-
if (hasProjectExpression(path)) {
259-
Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(path);
260-
while (matcher.find()) {
261-
String propertyName = matcher.group(0);
262-
if (!"${project.basedir}".equals(propertyName)) {
258+
final Deque<ActivationFrame> stk = new LinkedList<>();
259+
260+
final Supplier<String> pathSupplier = () -> {
261+
final boolean parallel = false;
262+
return StreamSupport.stream(((Iterable<ActivationFrame>) stk::descendingIterator).spliterator(), parallel)
263+
.map(f -> f.location)
264+
.collect(Collectors.joining("."));
265+
};
266+
final Supplier<InputLocation> locationSupplier = () -> {
267+
if (stk.size() < 2) {
268+
return null;
269+
}
270+
Iterator<ActivationFrame> f = stk.iterator();
271+
272+
String location = f.next().location;
273+
ActivationFrame parent = f.next();
274+
275+
return parent.parent.map(p -> p.getLocation(location)).orElse(null);
276+
};
277+
final Consumer<String> validator = s -> {
278+
if (hasProjectExpression(s)) {
279+
String path = pathSupplier.get();
280+
Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(s);
281+
while (matcher.find()) {
282+
String propertyName = matcher.group(0);
283+
284+
if (path.startsWith("activation.file.") && "${project.basedir}".equals(propertyName)) {
285+
continue;
286+
}
263287
addViolation(
264288
problems,
265289
Severity.WARNING,
266290
Version.V30,
267-
prefix + "activation.file." + location,
291+
prefix + path,
268292
null,
269-
"Failed to interpolate file location " + path + ": " + propertyName
293+
"Failed to interpolate profile activation property " + s + ": " + propertyName
270294
+ " expressions are not supported during profile activation.",
271-
file.getLocation(location));
295+
locationSupplier.get());
272296
}
273297
}
274-
}
298+
};
299+
Optional<Activation> root = Optional.of(activation);
300+
stk.push(new ActivationFrame("activation", root));
301+
root.map(Activation::getFile).ifPresent(fa -> {
302+
stk.push(new ActivationFrame("file", Optional.of(fa)));
303+
stk.push(new ActivationFrame("exists", Optional.empty()));
304+
validator.accept(fa.getExists());
305+
stk.peek().location = "missing";
306+
validator.accept(fa.getMissing());
307+
stk.pop();
308+
stk.pop();
309+
});
310+
root.map(Activation::getOs).ifPresent(oa -> {
311+
stk.push(new ActivationFrame("os", Optional.of(oa)));
312+
stk.push(new ActivationFrame("arch", Optional.empty()));
313+
validator.accept(oa.getArch());
314+
stk.peek().location = "family";
315+
validator.accept(oa.getFamily());
316+
stk.peek().location = "name";
317+
validator.accept(oa.getName());
318+
stk.peek().location = "version";
319+
validator.accept(oa.getVersion());
320+
stk.pop();
321+
stk.pop();
322+
});
323+
root.map(Activation::getProperty).ifPresent(pa -> {
324+
stk.push(new ActivationFrame("property", Optional.of(pa)));
325+
stk.push(new ActivationFrame("name", Optional.empty()));
326+
validator.accept(pa.getName());
327+
stk.peek().location = "value";
328+
validator.accept(pa.getValue());
329+
stk.pop();
330+
stk.pop();
331+
});
332+
root.map(Activation::getJdk).ifPresent(jdk -> {
333+
stk.push(new ActivationFrame("jdk", Optional.empty()));
334+
validator.accept(jdk);
335+
stk.pop();
336+
});
275337
}
276338

277339
private void validate20RawPlugins(

maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import java.io.InputStream;
2222
import java.util.List;
23+
import java.util.Properties;
2324
import java.util.function.UnaryOperator;
2425

2526
import junit.framework.TestCase;
@@ -761,24 +762,64 @@ public void testParentVersionRELEASE() throws Exception {
761762
result.getWarnings().get(0));
762763
}
763764

764-
public void testProfileActivationWithAllowedExpression() throws Exception {
765-
SimpleProblemCollector result = validateRaw("raw-model/profile-activation-file-with-allowed-expressions.xml");
765+
public void repositoryWithExpression() throws Exception {
766+
SimpleProblemCollector result = validateRaw("raw-model/repository-with-expression.xml");
767+
assertViolations(result, 0, 1, 0);
768+
assertEquals(
769+
"'repositories.repository.[repo].url' contains an expression but should be a constant.",
770+
result.getErrors().get(0));
771+
}
772+
773+
public void repositoryWithBasedirExpression() throws Exception {
774+
SimpleProblemCollector result = validateRaw("raw-model/repository-with-basedir-expression.xml");
766775
assertViolations(result, 0, 0, 0);
767776
}
768777

769-
public void testProfileActivationWithProjectExpression() throws Exception {
778+
public void profileActivationWithAllowedExpression() throws Exception {
779+
SimpleProblemCollector result = validateRaw(
780+
"raw-model/profile-activation-file-with-allowed-expressions.xml",
781+
mbr -> mbr.setUserProperties(new Properties() {
782+
private static final long serialVersionUID = 1L;
783+
784+
{
785+
setProperty("foo", "foo");
786+
setProperty("bar", "foo");
787+
}
788+
}));
789+
assertViolations(result, 0, 0, 0);
790+
}
791+
792+
public void profileActivationFileWithProjectExpression() throws Exception {
770793
SimpleProblemCollector result = validateRaw("raw-model/profile-activation-file-with-project-expressions.xml");
771794
assertViolations(result, 0, 0, 2);
772795

773796
assertEquals(
774797
"'profiles.profile[exists-project-version].activation.file.exists' "
775-
+ "Failed to interpolate file location ${project.version}/test.txt: "
798+
+ "Failed to interpolate profile activation property ${project.version}/test.txt: "
776799
+ "${project.version} expressions are not supported during profile activation.",
777800
result.getWarnings().get(0));
778801

779802
assertEquals(
780803
"'profiles.profile[missing-project-version].activation.file.missing' "
781-
+ "Failed to interpolate file location ${project.version}/test.txt: "
804+
+ "Failed to interpolate profile activation property ${project.version}/test.txt: "
805+
+ "${project.version} expressions are not supported during profile activation.",
806+
result.getWarnings().get(1));
807+
}
808+
809+
public void profileActivationPropertyWithProjectExpression() throws Exception {
810+
SimpleProblemCollector result =
811+
validateRaw("raw-model/profile-activation-property-with-project-expressions.xml");
812+
assertViolations(result, 0, 0, 2);
813+
814+
assertEquals(
815+
"'profiles.profile[property-name-project-version].activation.property.name' "
816+
+ "Failed to interpolate profile activation property ${project.version}: "
817+
+ "${project.version} expressions are not supported during profile activation.",
818+
result.getWarnings().get(0));
819+
820+
assertEquals(
821+
"'profiles.profile[property-value-project-version].activation.property.value' "
822+
+ "Failed to interpolate profile activation property ${project.version}: "
782823
+ "${project.version} expressions are not supported during profile activation.",
783824
result.getWarnings().get(1));
784825
}

maven-model-builder/src/test/resources/poms/validation/raw-model/profile-activation-file-with-allowed-expressions.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,23 @@ under the License.
6060
</activation>
6161
</profile>
6262

63+
<profile>
64+
<id>dynamic-property-available</id>
65+
<activation>
66+
<property>
67+
<name>${activationProperty}</name>
68+
</property>
69+
</activation>
70+
</profile>
71+
72+
<profile>
73+
<id>matches-another-property</id>
74+
<activation>
75+
<property>
76+
<name>foo</name>
77+
<value>${bar}</value>
78+
</property>
79+
</activation>
80+
</profile>
6381
</profiles>
6482
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
22+
<modelVersion>4.0.0</modelVersion>
23+
<artifactId>aid</artifactId>
24+
<groupId>gid</groupId>
25+
<version>0.1</version>
26+
<packaging>pom</packaging>
27+
28+
<profiles>
29+
30+
<profile>
31+
<id>property-name-project-version</id>
32+
<activation>
33+
<property>
34+
<name>${project.version}</name>
35+
</property>
36+
</activation>
37+
</profile>
38+
<profile>
39+
<id>property-value-project-version</id>
40+
<activation>
41+
<property>
42+
<name>project.version</name>
43+
<value>${project.version}</value>
44+
</property>
45+
</activation>
46+
</profile>
47+
48+
</profiles>
49+
</project>

0 commit comments

Comments
 (0)