diff --git a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelVersionProcessor.java b/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelVersionProcessor.java
deleted file mode 100644
index 8867441f09..0000000000
--- a/maven-api-impl/src/main/java/org/apache/maven/api/services/model/ModelVersionProcessor.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.api.services.model;
-
-import java.util.Properties;
-
-import org.apache.maven.api.services.ModelBuilderRequest;
-
-/**
- * Allows a fixed set of properties that are valid inside a version and that could be overwritten for example on the
- * commandline
- */
-public interface ModelVersionProcessor {
-
- /**
- * @param property the property to check
- * @return true
if this is a valid property for this processor
- */
- boolean isValidProperty(String property);
-
- /**
- * This method is responsible for examining the request and possibly overwrite of the valid properties in the model
- *
- * @param modelProperties
- * @param request
- */
- void overwriteModelProperties(Properties modelProperties, ModelBuilderRequest request);
-}
diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java
index b669ac9bb4..16544ae6a2 100644
--- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java
+++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelBuilder.java
@@ -26,7 +26,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -45,6 +44,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -233,6 +234,8 @@ public class DefaultModelBuilder implements ModelBuilder {
}
protected class DefaultModelBuilderSession implements ModelProblemCollector {
+ private static final Pattern REGEX = Pattern.compile("\\$\\{([^}]+)}");
+
final Session session;
final ModelBuilderRequest request;
final DefaultModelBuilderResult result;
@@ -557,58 +560,15 @@ public class DefaultModelBuilder implements ModelBuilder {
}
//
- // Transform raw model to build pom
- //
- Model transformFileToRaw(Model model) {
- Model.Builder builder = Model.newBuilder(model);
- builder = handleParent(model, builder);
- builder = handleReactorDependencies(model, builder);
- builder = handleCiFriendlyVersion(model, builder);
- return builder.build();
- }
-
- //
- // Infer parent information
- //
- Model.Builder handleParent(Model model, Model.Builder builder) {
- Parent parent = model.getParent();
- if (parent != null) {
- String version = parent.getVersion();
- String modVersion = replaceCiFriendlyVersion(version);
- if (!Objects.equals(version, modVersion)) {
- if (builder == null) {
- builder = Model.newBuilder(model);
- }
- builder.parent(parent.withVersion(modVersion));
- }
- }
- return builder;
- }
-
- //
- // CI friendly versions
- //
- Model.Builder handleCiFriendlyVersion(Model model, Model.Builder builder) {
- String version = model.getVersion();
- String modVersion = replaceCiFriendlyVersion(version);
- if (!Objects.equals(version, modVersion)) {
- if (builder == null) {
- builder = Model.newBuilder(model);
- }
- builder.version(modVersion);
- }
- return builder;
- }
-
- //
+ // Transform raw model to build pom.
// Infer inner reactor dependencies version
//
- Model.Builder handleReactorDependencies(Model model, Model.Builder builder) {
+ Model transformFileToRaw(Model model) {
List newDeps = new ArrayList<>();
boolean modified = false;
for (Dependency dep : model.getDependencies()) {
- Dependency.Builder depBuilder = null;
if (dep.getVersion() == null) {
+ Dependency.Builder depBuilder = null;
Model depModel = getRawModel(model.getPomFile(), dep.getGroupId(), dep.getArtifactId());
if (depModel != null) {
String version = depModel.getVersion();
@@ -629,30 +589,35 @@ public class DefaultModelBuilder implements ModelBuilder {
depBuilder.groupId(depGroupId).location("groupId", groupIdLocation);
}
}
- }
- if (depBuilder != null) {
- newDeps.add(depBuilder.build());
- modified = true;
- } else {
- newDeps.add(dep);
+ if (depBuilder != null) {
+ newDeps.add(depBuilder.build());
+ modified = true;
+ } else {
+ newDeps.add(dep);
+ }
}
}
- if (modified) {
- if (builder == null) {
- builder = Model.newBuilder(model);
- }
- builder.dependencies(newDeps);
- }
- return builder;
+ return modified ? model.withDependencies(newDeps) : model;
}
- String replaceCiFriendlyVersion(String version) {
+ String replaceCiFriendlyVersion(Map properties, String version) {
+ // TODO: we're using a simple regex here, but we should probably use
+ // a proper interpolation service to do the replacements
+ // once one is available in maven-api-impl
+ // https://issues.apache.org/jira/browse/MNG-8262
if (version != null) {
- for (String key : Arrays.asList("changelist", "revision", "sha1")) {
- String val = request.getUserProperties().get(key);
- if (val != null) {
- version = version.replace("${" + key + "}", val);
- }
+ Matcher matcher = REGEX.matcher(version);
+ if (matcher.find()) {
+ StringBuilder result = new StringBuilder();
+ do {
+ // extract the key inside ${}
+ String key = matcher.group(1);
+ // get replacement from the map, or use the original ${xy} if not found
+ String replacement = properties.getOrDefault(key, "\\" + matcher.group(0));
+ matcher.appendReplacement(result, replacement);
+ } while (matcher.find());
+ matcher.appendTail(result); // Append the remaining part of the string
+ return result.toString();
}
}
return version;
@@ -733,7 +698,6 @@ public class DefaultModelBuilder implements ModelBuilder {
return Stream.concat(Stream.of(r), r.getChildren().stream().flatMap(this::results));
}
- @SuppressWarnings("checkstyle:MethodLength")
private void loadFromRoot(Path root, Path top) {
try (PhasingExecutor executor = createExecutor()) {
DefaultModelBuilderResult r = Objects.equals(top, root) ? result : new DefaultModelBuilderResult();
@@ -1212,16 +1176,18 @@ public class DefaultModelBuilder implements ModelBuilder {
Model doReadFileModel() throws ModelBuilderException {
ModelSource modelSource = request.getSource();
Model model;
+ Path rootDirectory;
setSource(modelSource.getLocation());
logger.debug("Reading file model from " + modelSource.getLocation());
try {
boolean strict = request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_POM;
- // TODO: we do cache, but what if strict does not have the same value?
- Path rootDirectory;
try {
rootDirectory = request.getSession().getRootDirectory();
} catch (IllegalStateException ignore) {
rootDirectory = modelSource.getPath();
+ while (rootDirectory != null && !Files.isDirectory(rootDirectory)) {
+ rootDirectory = rootDirectory.getParent();
+ }
}
try (InputStream is = modelSource.openStream()) {
model = modelProcessor.read(XmlReaderRequest.builder()
@@ -1355,6 +1321,29 @@ public class DefaultModelBuilder implements ModelBuilder {
add(Severity.FATAL, Version.V41, "Error discovering subprojects", e);
}
}
+
+ // CI friendly version
+ // All expressions are interpolated using user properties and properties
+ // defined on the root project.
+ Map properties = new HashMap<>();
+ if (!Objects.equals(rootDirectory, model.getProjectDirectory())) {
+ Model rootModel = derive(ModelSource.fromPath(modelProcessor.locateExistingPom(rootDirectory)))
+ .readFileModel();
+ properties.putAll(rootModel.getProperties());
+ } else {
+ properties.putAll(model.getProperties());
+ }
+ properties.putAll(session.getUserProperties());
+ model = model.with()
+ .version(replaceCiFriendlyVersion(properties, model.getVersion()))
+ .parent(
+ model.getParent() != null
+ ? model.getParent()
+ .withVersion(replaceCiFriendlyVersion(
+ properties,
+ model.getParent().getVersion()))
+ : null)
+ .build();
}
for (var transformer : transformers) {
diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java
index 430ba510f1..c8e875be4b 100644
--- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java
+++ b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelValidator.java
@@ -70,7 +70,6 @@ import org.apache.maven.api.services.ModelProblem;
import org.apache.maven.api.services.ModelProblem.Version;
import org.apache.maven.api.services.ModelProblemCollector;
import org.apache.maven.api.services.model.ModelValidator;
-import org.apache.maven.api.services.model.ModelVersionProcessor;
import org.apache.maven.model.v4.MavenModelVersion;
import org.apache.maven.model.v4.MavenTransformer;
@@ -288,12 +287,8 @@ public class DefaultModelValidator implements ModelValidator {
private final Set validProfileIds = new HashSet<>();
- private final ModelVersionProcessor versionProcessor;
-
@Inject
- public DefaultModelValidator(ModelVersionProcessor versionProcessor) {
- this.versionProcessor = versionProcessor;
- }
+ public DefaultModelValidator() {}
@Override
@SuppressWarnings("checkstyle:MethodLength")
@@ -418,17 +413,42 @@ public class DefaultModelValidator implements ModelValidator {
Severity errOn30 = getSeverity(validationLevel, ModelValidator.VALIDATION_LEVEL_MAVEN_3_0);
- validateStringNoExpression("groupId", problems, Severity.WARNING, Version.V20, m.getGroupId(), m);
- if (parent == null) {
- validateStringNotEmpty("groupId", problems, Severity.FATAL, Version.V20, m.getGroupId(), m);
+ // The file pom may not contain the modelVersion yet, as it may be set later by the
+ // ModelVersionXMLFilter.
+ if (m.getModelVersion() != null && !m.getModelVersion().isEmpty()) {
+ validateModelVersion(problems, m.getModelVersion(), m, VALID_MODEL_VERSIONS);
}
- validateStringNoExpression("artifactId", problems, Severity.WARNING, Version.V20, m.getArtifactId(), m);
- validateStringNotEmpty("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m);
+ boolean isModelVersion41OrMore = !Objects.equals(ModelBuilder.MODEL_VERSION_4_0_0, m.getModelVersion());
+ if (isModelVersion41OrMore) {
+ validateStringNoExpression("groupId", problems, Severity.FATAL, Version.V41, m.getGroupId(), m);
- validateVersionNoExpression("version", problems, Severity.WARNING, Version.V20, m.getVersion(), m);
- if (parent == null) {
- validateStringNotEmpty("version", problems, Severity.FATAL, Version.V20, m.getVersion(), m);
+ validateStringNotEmpty("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m);
+ validateStringNoExpression("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m);
+
+ validateVersionNoExpression("version", problems, Severity.FATAL, Version.V41, m.getVersion(), m);
+
+ if (parent != null) {
+ validateStringNoExpression(
+ "groupId", problems, Severity.FATAL, Version.V41, parent.getGroupId(), m);
+ validateStringNoExpression(
+ "artifactId", problems, Severity.FATAL, Version.V41, parent.getArtifactId(), m);
+ validateVersionNoExpression(
+ "version", problems, Severity.FATAL, Version.V41, parent.getVersion(), m);
+ }
+ } else {
+ validateStringNoExpression("groupId", problems, Severity.WARNING, Version.V20, m.getGroupId(), m);
+ if (parent == null) {
+ validateStringNotEmpty("groupId", problems, Severity.FATAL, Version.V20, m.getGroupId(), m);
+ }
+
+ validateStringNoExpression("artifactId", problems, Severity.WARNING, Version.V20, m.getArtifactId(), m);
+ validateStringNotEmpty("artifactId", problems, Severity.FATAL, Version.V20, m.getArtifactId(), m);
+
+ validateVersionNoExpression("version", problems, Severity.WARNING, Version.V20, m.getVersion(), m);
+ if (parent == null) {
+ validateStringNotEmpty("version", problems, Severity.FATAL, Version.V20, m.getVersion(), m);
+ }
}
validate20RawDependencies(
@@ -1634,19 +1654,14 @@ public class DefaultModelValidator implements ModelValidator {
Matcher m = EXPRESSION_NAME_PATTERN.matcher(string.trim());
while (m.find()) {
- String property = m.group(1);
- if (!versionProcessor.isValidProperty(property)) {
- addViolation(
- problems,
- severity,
- version,
- fieldName,
- null,
- "contains an expression but should be a constant.",
- tracker);
-
- return false;
- }
+ addViolation(
+ problems,
+ severity,
+ version,
+ fieldName,
+ null,
+ "contains an expression but should be a constant.",
+ tracker);
}
return true;
diff --git a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelVersionProcessor.java b/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelVersionProcessor.java
deleted file mode 100644
index 592d5d1d0b..0000000000
--- a/maven-api-impl/src/main/java/org/apache/maven/internal/impl/model/DefaultModelVersionProcessor.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.internal.impl.model;
-
-import java.util.Map;
-import java.util.Properties;
-
-import org.apache.maven.api.di.Named;
-import org.apache.maven.api.di.Singleton;
-import org.apache.maven.api.services.ModelBuilderRequest;
-import org.apache.maven.api.services.model.ModelVersionProcessor;
-
-/**
- * Maven default implementation of the {@link ModelVersionProcessor} to support
- * CI Friendly Versions
- */
-@Named
-@Singleton
-public class DefaultModelVersionProcessor implements ModelVersionProcessor {
-
- private static final String SHA1_PROPERTY = "sha1";
-
- private static final String CHANGELIST_PROPERTY = "changelist";
-
- private static final String REVISION_PROPERTY = "revision";
-
- @Override
- public boolean isValidProperty(String property) {
- return REVISION_PROPERTY.equals(property)
- || CHANGELIST_PROPERTY.equals(property)
- || SHA1_PROPERTY.equals(property);
- }
-
- @Override
- public void overwriteModelProperties(Properties modelProperties, ModelBuilderRequest request) {
- Map props = request.getUserProperties();
- if (props.containsKey(REVISION_PROPERTY)) {
- modelProperties.put(REVISION_PROPERTY, props.get(REVISION_PROPERTY));
- }
- if (props.containsKey(CHANGELIST_PROPERTY)) {
- modelProperties.put(CHANGELIST_PROPERTY, props.get(CHANGELIST_PROPERTY));
- }
- if (props.containsKey(SHA1_PROPERTY)) {
- modelProperties.put(SHA1_PROPERTY, props.get(SHA1_PROPERTY));
- }
- }
-}
diff --git a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java
index 09eb857e85..04ad378d7c 100644
--- a/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java
+++ b/maven-api-impl/src/test/java/org/apache/maven/internal/impl/standalone/RepositorySystemSupplier.java
@@ -42,7 +42,6 @@ import org.apache.maven.internal.impl.model.DefaultModelNormalizer;
import org.apache.maven.internal.impl.model.DefaultModelPathTranslator;
import org.apache.maven.internal.impl.model.DefaultModelProcessor;
import org.apache.maven.internal.impl.model.DefaultModelValidator;
-import org.apache.maven.internal.impl.model.DefaultModelVersionProcessor;
import org.apache.maven.internal.impl.model.DefaultPathTranslator;
import org.apache.maven.internal.impl.model.DefaultPluginManagementInjector;
import org.apache.maven.internal.impl.model.DefaultProfileInjector;
@@ -1041,7 +1040,7 @@ public class RepositorySystemSupplier implements Supplier {
DefaultModelProcessor modelProcessor = new DefaultModelProcessor(new DefaultModelXmlFactory(), List.of());
return new DefaultModelBuilder(
modelProcessor,
- new DefaultModelValidator(new DefaultModelVersionProcessor()),
+ new DefaultModelValidator(),
new DefaultModelNormalizer(),
new DefaultModelInterpolator(
new DefaultPathTranslator(), new DefaultUrlNormalizer(), new DefaultRootLocator()),
diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java
index a2923aadca..649ce35d07 100644
--- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java
+++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenRepositorySystemSupplier.java
@@ -42,7 +42,6 @@ import org.apache.maven.internal.impl.model.DefaultModelNormalizer;
import org.apache.maven.internal.impl.model.DefaultModelPathTranslator;
import org.apache.maven.internal.impl.model.DefaultModelProcessor;
import org.apache.maven.internal.impl.model.DefaultModelValidator;
-import org.apache.maven.internal.impl.model.DefaultModelVersionProcessor;
import org.apache.maven.internal.impl.model.DefaultPathTranslator;
import org.apache.maven.internal.impl.model.DefaultPluginManagementInjector;
import org.apache.maven.internal.impl.model.DefaultProfileInjector;
@@ -1044,7 +1043,7 @@ public class MavenRepositorySystemSupplier implements Supplier
DefaultModelProcessor modelProcessor = new DefaultModelProcessor(new DefaultModelXmlFactory(), List.of());
return new DefaultModelBuilder(
modelProcessor,
- new DefaultModelValidator(new DefaultModelVersionProcessor()),
+ new DefaultModelValidator(),
new DefaultModelNormalizer(),
new DefaultModelInterpolator(
new DefaultPathTranslator(), new DefaultUrlNormalizer(), new DefaultRootLocator()),