blob: 246e2f8b1314aef2a86b91222579ddf3284b986f [file] [log] [blame]
/*
* 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.model.validation;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
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.StreamSupport;
import org.apache.maven.api.model.Activation;
import org.apache.maven.api.model.ActivationFile;
import org.apache.maven.api.model.ActivationOS;
import org.apache.maven.api.model.ActivationProperty;
import org.apache.maven.api.model.Build;
import org.apache.maven.api.model.BuildBase;
import org.apache.maven.api.model.Dependency;
import org.apache.maven.api.model.DependencyManagement;
import org.apache.maven.api.model.DistributionManagement;
import org.apache.maven.api.model.Exclusion;
import org.apache.maven.api.model.InputLocation;
import org.apache.maven.api.model.InputLocationTracker;
import org.apache.maven.api.model.Parent;
import org.apache.maven.api.model.Plugin;
import org.apache.maven.api.model.PluginExecution;
import org.apache.maven.api.model.PluginManagement;
import org.apache.maven.api.model.Profile;
import org.apache.maven.api.model.ReportPlugin;
import org.apache.maven.api.model.Reporting;
import org.apache.maven.api.model.Repository;
import org.apache.maven.api.model.Resource;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.model.building.ModelProblem.Severity;
import org.apache.maven.model.building.ModelProblem.Version;
import org.apache.maven.model.building.ModelProblemCollector;
import org.apache.maven.model.building.ModelProblemCollectorRequest;
import org.apache.maven.model.interpolation.ModelVersionProcessor;
import org.apache.maven.model.v4.MavenModelVersion;
import org.apache.maven.model.v4.MavenTransformer;
/**
*/
@Named
@Singleton
public class DefaultModelValidator implements ModelValidator {
public static final List<String> VALID_MODEL_VERSIONS =
Collections.unmodifiableList(Arrays.asList("4.0.0", "4.1.0"));
private static final Pattern EXPRESSION_NAME_PATTERN = Pattern.compile("\\$\\{(.+?)}");
private static final Pattern EXPRESSION_PROJECT_NAME_PATTERN = Pattern.compile("\\$\\{(project.+?)}");
private static final String ILLEGAL_FS_CHARS = "\\/:\"<>|?*";
private static final String ILLEGAL_VERSION_CHARS = ILLEGAL_FS_CHARS;
private static final String ILLEGAL_REPO_ID_CHARS = ILLEGAL_FS_CHARS;
private static final String EMPTY = "";
private record ActivationFrame(String location, Optional<? extends InputLocationTracker> parent) {}
private static class ActivationWalker extends MavenTransformer {
private final Deque<ActivationFrame> stk;
ActivationWalker(Deque<ActivationFrame> stk, UnaryOperator<String> transformer) {
super(transformer);
this.stk = stk;
}
private ActivationFrame nextFrame(String property) {
return new ActivationFrame(property, Optional.empty());
}
private <P> ActivationFrame nextFrame(String property, Function<P, InputLocationTracker> child) {
@SuppressWarnings("unchecked")
final Optional<P> parent = (Optional<P>) stk.peek().parent;
return new ActivationFrame(property, parent.map(child));
}
@Override
public Activation transformActivation(Activation target) {
stk.push(new ActivationFrame("activation", Optional.of(target)));
try {
return super.transformActivation(target);
} finally {
stk.pop();
}
}
@Override
protected Activation.Builder transformActivation_ActiveByDefault(
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
return builder;
}
@Override
protected Activation.Builder transformActivation_File(
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
stk.push(nextFrame("file", Activation::getFile));
Optional.ofNullable(target.getFile());
try {
return super.transformActivation_File(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected ActivationFile.Builder transformActivationFile_Exists(
Supplier<? extends ActivationFile.Builder> creator,
ActivationFile.Builder builder,
ActivationFile target) {
stk.push(nextFrame("exists"));
try {
return super.transformActivationFile_Exists(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected ActivationFile.Builder transformActivationFile_Missing(
Supplier<? extends ActivationFile.Builder> creator,
ActivationFile.Builder builder,
ActivationFile target) {
stk.push(nextFrame("missing"));
try {
return super.transformActivationFile_Missing(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected Activation.Builder transformActivation_Jdk(
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
stk.push(nextFrame("jdk"));
try {
return super.transformActivation_Jdk(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected Activation.Builder transformActivation_Os(
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
stk.push(nextFrame("os", Activation::getOs));
try {
return super.transformActivation_Os(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected ActivationOS.Builder transformActivationOS_Arch(
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
stk.push(nextFrame("arch"));
try {
return super.transformActivationOS_Arch(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected ActivationOS.Builder transformActivationOS_Family(
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
stk.push(nextFrame("family"));
try {
return super.transformActivationOS_Family(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected ActivationOS.Builder transformActivationOS_Name(
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
stk.push(nextFrame("name"));
try {
return super.transformActivationOS_Name(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected ActivationOS.Builder transformActivationOS_Version(
Supplier<? extends ActivationOS.Builder> creator, ActivationOS.Builder builder, ActivationOS target) {
stk.push(nextFrame("version"));
try {
return super.transformActivationOS_Version(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected Activation.Builder transformActivation_Packaging(
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
stk.push(nextFrame("packaging"));
try {
return super.transformActivation_Packaging(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected Activation.Builder transformActivation_Property(
Supplier<? extends Activation.Builder> creator, Activation.Builder builder, Activation target) {
stk.push(nextFrame("property", Activation::getProperty));
try {
return super.transformActivation_Property(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected ActivationProperty.Builder transformActivationProperty_Name(
Supplier<? extends ActivationProperty.Builder> creator,
ActivationProperty.Builder builder,
ActivationProperty target) {
stk.push(nextFrame("name"));
try {
return super.transformActivationProperty_Name(creator, builder, target);
} finally {
stk.pop();
}
}
@Override
protected ActivationProperty.Builder transformActivationProperty_Value(
Supplier<? extends ActivationProperty.Builder> creator,
ActivationProperty.Builder builder,
ActivationProperty target) {
stk.push(nextFrame("value"));
try {
return super.transformActivationProperty_Value(creator, builder, target);
} finally {
stk.pop();
}
}
}
private final Set<String> validCoordinateIds = new HashSet<>();
private final Set<String> validProfileIds = new HashSet<>();
private final ModelVersionProcessor versionProcessor;
@Inject
public DefaultModelValidator(ModelVersionProcessor versionProcessor) {
this.versionProcessor = versionProcessor;
}
@Override
public void validateFileModel(Model ma, ModelBuildingRequest request, ModelProblemCollector problems) {
org.apache.maven.api.model.Model m = ma.getDelegate();
Parent parent = m.getParent();
if (parent != null) {
validateStringNotEmpty(
"parent.groupId", problems, Severity.FATAL, Version.BASE, parent.getGroupId(), parent);
validateStringNotEmpty(
"parent.artifactId", problems, Severity.FATAL, Version.BASE, parent.getArtifactId(), parent);
if (equals(parent.getGroupId(), m.getGroupId()) && equals(parent.getArtifactId(), m.getArtifactId())) {
addViolation(
problems,
Severity.FATAL,
Version.BASE,
"parent.artifactId",
null,
"must be changed"
+ ", the parent element cannot have the same groupId:artifactId as the project.",
parent);
}
if (equals("LATEST", parent.getVersion()) || equals("RELEASE", parent.getVersion())) {
addViolation(
problems,
Severity.WARNING,
Version.BASE,
"parent.version",
null,
"is either LATEST or RELEASE (both of them are being deprecated)",
parent);
}
}
if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
Set<String> modules = new HashSet<>();
for (int i = 0, n = m.getModules().size(); i < n; i++) {
String module = m.getModules().get(i);
if (!modules.add(module)) {
addViolation(
problems,
Severity.ERROR,
Version.V20,
"modules.module[" + i + "]",
null,
"specifies duplicate child module " + module,
m.getLocation("modules"));
}
}
Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
// 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("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(problems, m.getDependencies(), "dependencies.dependency.", EMPTY, request);
validate20RawDependenciesSelfReferencing(
problems, m, m.getDependencies(), "dependencies.dependency", request);
if (m.getDependencyManagement() != null) {
validate20RawDependencies(
problems,
m.getDependencyManagement().getDependencies(),
"dependencyManagement.dependencies.dependency.",
EMPTY,
request);
}
validateRawRepositories(problems, m.getRepositories(), "repositories.repository.", EMPTY, request);
validateRawRepositories(
problems, m.getPluginRepositories(), "pluginRepositories.pluginRepository.", EMPTY, request);
Build build = m.getBuild();
if (build != null) {
validate20RawPlugins(problems, build.getPlugins(), "build.plugins.plugin.", EMPTY, request);
PluginManagement mgmt = build.getPluginManagement();
if (mgmt != null) {
validate20RawPlugins(
problems, mgmt.getPlugins(), "build.pluginManagement.plugins.plugin.", EMPTY, request);
}
}
Set<String> profileIds = new HashSet<>();
for (Profile profile : m.getProfiles()) {
String prefix = "profiles.profile[" + profile.getId() + "].";
validateProfileId(prefix, "id", problems, Severity.ERROR, Version.V40, profile.getId(), null, m);
if (!profileIds.add(profile.getId())) {
addViolation(
problems,
errOn30,
Version.V20,
"profiles.profile.id",
null,
"must be unique but found duplicate profile with id " + profile.getId(),
profile);
}
validate30RawProfileActivation(problems, profile.getActivation(), prefix);
validate20RawDependencies(
problems, profile.getDependencies(), prefix, "dependencies.dependency.", request);
if (profile.getDependencyManagement() != null) {
validate20RawDependencies(
problems,
profile.getDependencyManagement().getDependencies(),
prefix,
"dependencyManagement.dependencies.dependency.",
request);
}
validateRawRepositories(
problems, profile.getRepositories(), prefix, "repositories.repository.", request);
validateRawRepositories(
problems,
profile.getPluginRepositories(),
prefix,
"pluginRepositories.pluginRepository.",
request);
BuildBase buildBase = profile.getBuild();
if (buildBase != null) {
validate20RawPlugins(problems, buildBase.getPlugins(), prefix, "plugins.plugin.", request);
PluginManagement mgmt = buildBase.getPluginManagement();
if (mgmt != null) {
validate20RawPlugins(
problems, mgmt.getPlugins(), prefix, "pluginManagement.plugins.plugin.", request);
}
}
}
}
}
@Override
public void validateRawModel(Model ma, ModelBuildingRequest request, ModelProblemCollector problems) {
org.apache.maven.api.model.Model m = ma.getDelegate();
// [MNG-6074] Maven should produce an error if no model version has been set in a POM file used to build an
// effective model.
//
// As of 3.4, the model version is mandatory even in raw models. The XML element still is optional in the
// XML schema and this will not change anytime soon. We do not want to build effective models based on
// models without a version starting with 3.4.
validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.V20, m.getModelVersion(), m);
validateModelVersion(problems, m.getModelVersion(), m, VALID_MODEL_VERSIONS);
String minVersion = new MavenModelVersion().getModelVersion(m);
if (m.getModelVersion() != null && compareModelVersions(minVersion, m.getModelVersion()) > 0) {
addViolation(
problems,
Severity.FATAL,
Version.V40,
"model",
null,
"the model contains elements that require a model version of " + minVersion,
m);
}
Parent parent = m.getParent();
if (parent != null) {
validateStringNotEmpty(
"parent.version", problems, Severity.FATAL, Version.BASE, parent.getVersion(), parent);
}
}
private void validate30RawProfileActivation(ModelProblemCollector problems, Activation activation, String prefix) {
if (activation == null) {
return;
}
final Deque<ActivationFrame> stk = new LinkedList<>();
final Supplier<String> pathSupplier = () -> {
final boolean parallel = false;
return StreamSupport.stream(((Iterable<ActivationFrame>) stk::descendingIterator).spliterator(), parallel)
.map(ActivationFrame::location)
.collect(Collectors.joining("."));
};
final Supplier<InputLocation> locationSupplier = () -> {
if (stk.size() < 2) {
return null;
}
Iterator<ActivationFrame> f = stk.iterator();
String location = f.next().location;
ActivationFrame parent = f.next();
return parent.parent.map(p -> p.getLocation(location)).orElse(null);
};
final UnaryOperator<String> transformer = s -> {
if (hasProjectExpression(s)) {
String path = pathSupplier.get();
Matcher matcher = EXPRESSION_PROJECT_NAME_PATTERN.matcher(s);
while (matcher.find()) {
String propertyName = matcher.group(0);
if (path.startsWith("activation.file.") && "${project.basedir}".equals(propertyName)) {
continue;
}
addViolation(
problems,
Severity.WARNING,
Version.V30,
prefix + path,
null,
"Failed to interpolate profile activation property " + s + ": " + propertyName
+ " expressions are not supported during profile activation.",
locationSupplier.get());
}
}
return s;
};
new ActivationWalker(stk, transformer).transformActivation(activation);
}
private void validate20RawPlugins(
ModelProblemCollector problems,
List<Plugin> plugins,
String prefix,
String prefix2,
ModelBuildingRequest request) {
Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
Map<String, Plugin> index = new HashMap<>();
for (Plugin plugin : plugins) {
if (plugin.getGroupId() == null
|| (plugin.getGroupId() != null
&& plugin.getGroupId().trim().isEmpty())) {
addViolation(
problems,
Severity.FATAL,
Version.V20,
prefix + prefix2 + "(groupId:artifactId)",
null,
"groupId of a plugin must be defined. ",
plugin);
}
if (plugin.getArtifactId() == null
|| (plugin.getArtifactId() != null
&& plugin.getArtifactId().trim().isEmpty())) {
addViolation(
problems,
Severity.FATAL,
Version.V20,
prefix + prefix2 + "(groupId:artifactId)",
null,
"artifactId of a plugin must be defined. ",
plugin);
}
// This will catch cases like <version></version> or <version/>
if (plugin.getVersion() != null && plugin.getVersion().trim().isEmpty()) {
addViolation(
problems,
Severity.FATAL,
Version.V20,
prefix + prefix2 + "(groupId:artifactId)",
null,
"version of a plugin must be defined. ",
plugin);
}
String key = plugin.getKey();
Plugin existing = index.get(key);
if (existing != null) {
addViolation(
problems,
errOn31,
Version.V20,
prefix + prefix2 + "(groupId:artifactId)",
null,
"must be unique but found duplicate declaration of plugin " + key,
plugin);
} else {
index.put(key, plugin);
}
Set<String> executionIds = new HashSet<>();
for (PluginExecution exec : plugin.getExecutions()) {
if (!executionIds.add(exec.getId())) {
addViolation(
problems,
Severity.ERROR,
Version.V20,
prefix + prefix2 + "[" + plugin.getKey() + "].executions.execution.id",
null,
"must be unique but found duplicate execution with id " + exec.getId(),
exec);
}
}
}
}
@Override
public void validateEffectiveModel(Model ma, ModelBuildingRequest request, ModelProblemCollector problems) {
org.apache.maven.api.model.Model m = ma.getDelegate();
validateStringNotEmpty("modelVersion", problems, Severity.ERROR, Version.BASE, m.getModelVersion(), m);
validateCoordinateId("groupId", problems, m.getGroupId(), m);
validateCoordinateId("artifactId", problems, m.getArtifactId(), m);
validateStringNotEmpty("packaging", problems, Severity.ERROR, Version.BASE, m.getPackaging(), m);
if (!m.getModules().isEmpty()) {
if (!"pom".equals(m.getPackaging())) {
addViolation(
problems,
Severity.ERROR,
Version.BASE,
"packaging",
null,
"with value '" + m.getPackaging() + "' is invalid. Aggregator projects "
+ "require 'pom' as packaging.",
m);
}
for (int i = 0, n = m.getModules().size(); i < n; i++) {
String module = m.getModules().get(i);
boolean isBlankModule = true;
if (module != null) {
for (int j = 0; j < module.length(); j++) {
if (!Character.isWhitespace(module.charAt(j))) {
isBlankModule = false;
}
}
}
if (isBlankModule) {
addViolation(
problems,
Severity.ERROR,
Version.BASE,
"modules.module[" + i + "]",
null,
"has been specified without a path to the project directory.",
m.getLocation("modules"));
}
}
}
validateStringNotEmpty("version", problems, Severity.ERROR, Version.BASE, m.getVersion(), m);
Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
validateEffectiveDependencies(problems, m, m.getDependencies(), false, request);
DependencyManagement mgmt = m.getDependencyManagement();
if (mgmt != null) {
validateEffectiveDependencies(problems, m, mgmt.getDependencies(), true, request);
}
if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
validateBannedCharacters(
EMPTY, "version", problems, errOn31, Version.V20, m.getVersion(), null, m, ILLEGAL_VERSION_CHARS);
validate20ProperSnapshotVersion("version", problems, errOn31, Version.V20, m.getVersion(), null, m);
Build build = m.getBuild();
if (build != null) {
for (Plugin p : build.getPlugins()) {
validateStringNotEmpty(
"build.plugins.plugin.artifactId",
problems,
Severity.ERROR,
Version.V20,
p.getArtifactId(),
p);
validateStringNotEmpty(
"build.plugins.plugin.groupId", problems, Severity.ERROR, Version.V20, p.getGroupId(), p);
validate20PluginVersion(
"build.plugins.plugin.version", problems, p.getVersion(), p.getKey(), p, request);
validateBoolean(
"build.plugins.plugin.inherited",
EMPTY,
problems,
errOn30,
Version.V20,
p.getInherited(),
p.getKey(),
p);
validateBoolean(
"build.plugins.plugin.extensions",
EMPTY,
problems,
errOn30,
Version.V20,
p.getExtensions(),
p.getKey(),
p);
validate20EffectivePluginDependencies(problems, p, request);
}
validate20RawResources(problems, build.getResources(), "build.resources.resource.", request);
validate20RawResources(
problems, build.getTestResources(), "build.testResources.testResource.", request);
}
Reporting reporting = m.getReporting();
if (reporting != null) {
for (ReportPlugin p : reporting.getPlugins()) {
validateStringNotEmpty(
"reporting.plugins.plugin.artifactId",
problems,
Severity.ERROR,
Version.V20,
p.getArtifactId(),
p);
validateStringNotEmpty(
"reporting.plugins.plugin.groupId",
problems,
Severity.ERROR,
Version.V20,
p.getGroupId(),
p);
}
}
for (Repository repository : m.getRepositories()) {
validate20EffectiveRepository(problems, repository, "repositories.repository.", request);
}
for (Repository repository : m.getPluginRepositories()) {
validate20EffectiveRepository(problems, repository, "pluginRepositories.pluginRepository.", request);
}
DistributionManagement distMgmt = m.getDistributionManagement();
if (distMgmt != null) {
if (distMgmt.getStatus() != null) {
addViolation(
problems,
Severity.ERROR,
Version.V20,
"distributionManagement.status",
null,
"must not be specified.",
distMgmt);
}
validate20EffectiveRepository(
problems, distMgmt.getRepository(), "distributionManagement.repository.", request);
validate20EffectiveRepository(
problems,
distMgmt.getSnapshotRepository(),
"distributionManagement.snapshotRepository.",
request);
}
}
}
private void validate20RawDependencies(
ModelProblemCollector problems,
List<Dependency> dependencies,
String prefix,
String prefix2,
ModelBuildingRequest request) {
Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
Map<String, Dependency> index = new HashMap<>();
for (Dependency dependency : dependencies) {
String key = dependency.getManagementKey();
if ("import".equals(dependency.getScope())) {
if (!"pom".equals(dependency.getType())) {
addViolation(
problems,
Severity.WARNING,
Version.V20,
prefix + prefix2 + "type",
key,
"must be 'pom' to import the managed dependencies.",
dependency);
} else if (dependency.getClassifier() != null
&& !dependency.getClassifier().isEmpty()) {
addViolation(
problems,
errOn30,
Version.V20,
prefix + prefix2 + "classifier",
key,
"must be empty, imported POM cannot have a classifier.",
dependency);
}
} else if ("system".equals(dependency.getScope())) {
if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1) {
addViolation(
problems,
Severity.WARNING,
Version.V31,
prefix + prefix2 + "scope",
key,
"declares usage of deprecated 'system' scope ",
dependency);
}
String sysPath = dependency.getSystemPath();
if (sysPath != null && !sysPath.isEmpty()) {
if (!hasExpression(sysPath)) {
addViolation(
problems,
Severity.WARNING,
Version.V20,
prefix + prefix2 + "systemPath",
key,
"should use a variable instead of a hard-coded path " + sysPath,
dependency);
} else if (sysPath.contains("${basedir}") || sysPath.contains("${project.basedir}")) {
addViolation(
problems,
Severity.WARNING,
Version.V20,
prefix + prefix2 + "systemPath",
key,
"should not point at files within the project directory, " + sysPath
+ " will be unresolvable by dependent projects",
dependency);
}
}
}
if (equals("LATEST", dependency.getVersion()) || equals("RELEASE", dependency.getVersion())) {
addViolation(
problems,
Severity.WARNING,
Version.BASE,
prefix + prefix2 + "version",
key,
"is either LATEST or RELEASE (both of them are being deprecated)",
dependency);
}
Dependency existing = index.get(key);
if (existing != null) {
String msg;
if (equals(existing.getVersion(), dependency.getVersion())) {
msg = "duplicate declaration of version " + Objects.toString(dependency.getVersion(), "(?)");
} else {
msg = "version " + Objects.toString(existing.getVersion(), "(?)") + " vs "
+ Objects.toString(dependency.getVersion(), "(?)");
}
addViolation(
problems,
errOn31,
Version.V20,
prefix + prefix2 + "(groupId:artifactId:type:classifier)",
null,
"must be unique: " + key + " -> " + msg,
dependency);
} else {
index.put(key, dependency);
}
}
}
private void validate20RawDependenciesSelfReferencing(
ModelProblemCollector problems,
org.apache.maven.api.model.Model m,
List<Dependency> dependencies,
String prefix,
ModelBuildingRequest request) {
// We only check for groupId/artifactId/version/classifier cause if there is another
// module with the same groupId/artifactId/version/classifier this will fail the build
// earlier like "Project '...' is duplicated in the reactor.
// So it is sufficient to check only groupId/artifactId/version/classifier and not the
// packaging type.
for (Dependency dependency : dependencies) {
String key = dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getVersion()
+ (dependency.getClassifier() != null ? ":" + dependency.getClassifier() : EMPTY);
String mKey = m.getGroupId() + ":" + m.getArtifactId() + ":" + m.getVersion();
if (key.equals(mKey)) {
// This means a module which is build has a dependency which has the same
// groupId, artifactId, version and classifier coordinates. This is in consequence
// a self reference or in other words a circular reference which can not being resolved.
addViolation(
problems,
Severity.FATAL,
Version.V31,
prefix + "[" + key + "]",
key,
"is referencing itself.",
dependency);
}
}
}
private void validateEffectiveDependencies(
ModelProblemCollector problems,
org.apache.maven.api.model.Model m,
List<Dependency> dependencies,
boolean management,
ModelBuildingRequest request) {
Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
String prefix = management ? "dependencyManagement.dependencies.dependency." : "dependencies.dependency.";
for (Dependency d : dependencies) {
validateEffectiveDependency(problems, d, management, prefix, request);
if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
validateBoolean(
prefix, "optional", problems, errOn30, Version.V20, d.getOptional(), d.getManagementKey(), d);
if (!management) {
validateVersion(
prefix, "version", problems, errOn30, Version.V20, d.getVersion(), d.getManagementKey(), d);
/*
* TODO Extensions like Flex Mojos use custom scopes like "merged", "internal", "external", etc. In
* order to don't break backward-compat with those, only warn but don't error out.
*/
validateEnum(
prefix,
"scope",
problems,
Severity.WARNING,
Version.V20,
d.getScope(),
d.getManagementKey(),
d,
"provided",
"compile",
"runtime",
"test",
"system");
validateEffectiveModelAgainstDependency(prefix, problems, m, d, request);
} else {
validateEnum(
prefix,
"scope",
problems,
Severity.WARNING,
Version.V20,
d.getScope(),
d.getManagementKey(),
d,
"provided",
"compile",
"runtime",
"test",
"system",
"import");
}
}
}
}
private void validateEffectiveModelAgainstDependency(
String prefix,
ModelProblemCollector problems,
org.apache.maven.api.model.Model m,
Dependency d,
ModelBuildingRequest request) {
String key = d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion()
+ (d.getClassifier() != null ? ":" + d.getClassifier() : EMPTY);
String mKey = m.getGroupId() + ":" + m.getArtifactId() + ":" + m.getVersion();
if (key.equals(mKey)) {
// This means a module which is build has a dependency which has the same
// groupId, artifactId, version and classifier coordinates. This is in consequence
// a self reference or in other words a circular reference which can not being resolved.
addViolation(
problems, Severity.FATAL, Version.V31, prefix + "[" + key + "]", key, "is referencing itself.", d);
}
}
private void validate20EffectivePluginDependencies(
ModelProblemCollector problems, Plugin plugin, ModelBuildingRequest request) {
List<Dependency> dependencies = plugin.getDependencies();
if (!dependencies.isEmpty()) {
String prefix = "build.plugins.plugin[" + plugin.getKey() + "].dependencies.dependency.";
Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
for (Dependency d : dependencies) {
validateEffectiveDependency(problems, d, false, prefix, request);
validateVersion(
prefix, "version", problems, errOn30, Version.BASE, d.getVersion(), d.getManagementKey(), d);
validateEnum(
prefix,
"scope",
problems,
errOn30,
Version.BASE,
d.getScope(),
d.getManagementKey(),
d,
"compile",
"runtime",
"system");
}
}
}
private void validateEffectiveDependency(
ModelProblemCollector problems,
Dependency d,
boolean management,
String prefix,
ModelBuildingRequest request) {
validateCoordinateId(
prefix,
"artifactId",
problems,
Severity.ERROR,
Version.BASE,
d.getArtifactId(),
d.getManagementKey(),
d);
validateCoordinateId(
prefix, "groupId", problems, Severity.ERROR, Version.BASE, d.getGroupId(), d.getManagementKey(), d);
if (!management) {
validateStringNotEmpty(
prefix, "type", problems, Severity.ERROR, Version.BASE, d.getType(), d.getManagementKey(), d);
validateDependencyVersion(problems, d, prefix);
}
if ("system".equals(d.getScope())) {
String systemPath = d.getSystemPath();
if (systemPath == null || systemPath.isEmpty()) {
addViolation(
problems,
Severity.ERROR,
Version.BASE,
prefix + "systemPath",
d.getManagementKey(),
"is missing.",
d);
} else {
File sysFile = new File(systemPath);
if (!sysFile.isAbsolute()) {
addViolation(
problems,
Severity.ERROR,
Version.BASE,
prefix + "systemPath",
d.getManagementKey(),
"must specify an absolute path but is " + systemPath,
d);
} else if (!sysFile.isFile()) {
String msg = "refers to a non-existing file " + sysFile.getAbsolutePath();
systemPath = systemPath.replace('/', File.separatorChar).replace('\\', File.separatorChar);
String jdkHome =
request.getSystemProperties().getProperty("java.home", EMPTY) + File.separator + "..";
if (systemPath.startsWith(jdkHome)) {
msg += ". Please verify that you run Maven using a JDK and not just a JRE.";
}
addViolation(
problems,
Severity.WARNING,
Version.BASE,
prefix + "systemPath",
d.getManagementKey(),
msg,
d);
}
}
} else if (d.getSystemPath() != null && !d.getSystemPath().isEmpty()) {
addViolation(
problems,
Severity.ERROR,
Version.BASE,
prefix + "systemPath",
d.getManagementKey(),
"must be omitted. This field may only be specified for a dependency with system scope.",
d);
}
if (request.getValidationLevel() >= ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_2_0) {
for (Exclusion exclusion : d.getExclusions()) {
if (request.getValidationLevel() < ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0) {
validateCoordinateId(
prefix,
"exclusions.exclusion.groupId",
problems,
Severity.WARNING,
Version.V20,
exclusion.getGroupId(),
d.getManagementKey(),
exclusion);
validateCoordinateId(
prefix,
"exclusions.exclusion.artifactId",
problems,
Severity.WARNING,
Version.V20,
exclusion.getArtifactId(),
d.getManagementKey(),
exclusion);
} else {
validateCoordinateIdWithWildcards(
prefix,
"exclusions.exclusion.groupId",
problems,
Severity.WARNING,
Version.V30,
exclusion.getGroupId(),
d.getManagementKey(),
exclusion);
validateCoordinateIdWithWildcards(
prefix,
"exclusions.exclusion.artifactId",
problems,
Severity.WARNING,
Version.V30,
exclusion.getArtifactId(),
d.getManagementKey(),
exclusion);
}
}
}
}
/**
* @since 3.2.4
*/
protected void validateDependencyVersion(ModelProblemCollector problems, Dependency d, String prefix) {
validateStringNotEmpty(
prefix, "version", problems, Severity.ERROR, Version.BASE, d.getVersion(), d.getManagementKey(), d);
}
private void validateRawRepositories(
ModelProblemCollector problems,
List<Repository> repositories,
String prefix,
String prefix2,
ModelBuildingRequest request) {
Map<String, Repository> index = new HashMap<>();
for (Repository repository : repositories) {
validateStringNotEmpty(
prefix, prefix2, "id", problems, Severity.ERROR, Version.V20, repository.getId(), null, repository);
if (validateStringNotEmpty(
prefix,
prefix2,
"[" + repository.getId() + "].url",
problems,
Severity.ERROR,
Version.V20,
repository.getUrl(),
null,
repository)) {
// only allow ${basedir}, ${project.basedir} or ${project.baseUri}
Matcher m = EXPRESSION_NAME_PATTERN.matcher(repository.getUrl());
while (m.find()) {
if (!("basedir".equals(m.group(1))
|| "project.basedir".equals(m.group(1))
|| "project.baseUri".equals(m.group(1)))) {
validateStringNoExpression(
prefix + prefix2 + "[" + repository.getId() + "].url",
problems,
Severity.ERROR,
Version.V40,
repository.getUrl(),
repository);
break;
}
}
}
String key = repository.getId();
Repository existing = index.get(key);
if (existing != null) {
Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
addViolation(
problems,
errOn30,
Version.V20,
prefix + prefix2 + "id",
null,
"must be unique: " + repository.getId() + " -> " + existing.getUrl() + " vs "
+ repository.getUrl(),
repository);
} else {
index.put(key, repository);
}
}
}
private void validate20EffectiveRepository(
ModelProblemCollector problems, Repository repository, String prefix, ModelBuildingRequest request) {
if (repository != null) {
Severity errOn31 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1);
validateBannedCharacters(
prefix,
"id",
problems,
errOn31,
Version.V20,
repository.getId(),
null,
repository,
ILLEGAL_REPO_ID_CHARS);
if ("local".equals(repository.getId())) {
addViolation(
problems,
errOn31,
Version.V20,
prefix + "id",
null,
"must not be 'local'" + ", this identifier is reserved for the local repository"
+ ", using it for other repositories will corrupt your repository metadata.",
repository);
}
if ("legacy".equals(repository.getLayout())) {
addViolation(
problems,
Severity.WARNING,
Version.V20,
prefix + "layout",
repository.getId(),
"uses the unsupported value 'legacy', artifact resolution might fail.",
repository);
}
}
}
private void validate20RawResources(
ModelProblemCollector problems, List<Resource> resources, String prefix, ModelBuildingRequest request) {
Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
for (Resource resource : resources) {
validateStringNotEmpty(
prefix,
"directory",
problems,
Severity.ERROR,
Version.V20,
resource.getDirectory(),
null,
resource);
validateBoolean(
prefix,
"filtering",
problems,
errOn30,
Version.V20,
resource.getFiltering(),
resource.getDirectory(),
resource);
}
}
// ----------------------------------------------------------------------
// Field validation
// ----------------------------------------------------------------------
private boolean validateCoordinateId(
String fieldName, ModelProblemCollector problems, String id, InputLocationTracker tracker) {
return validateCoordinateId(EMPTY, fieldName, problems, Severity.ERROR, Version.BASE, id, null, tracker);
}
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateCoordinateId(
String prefix,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String id,
String sourceHint,
InputLocationTracker tracker) {
if (validCoordinateIds.contains(id)) {
return true;
}
if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
return false;
} else {
if (!isValidCoordinateId(id)) {
addViolation(
problems,
severity,
version,
prefix + fieldName,
sourceHint,
"with value '" + id + "' does not match a valid coordinate id pattern.",
tracker);
return false;
}
validCoordinateIds.add(id);
return true;
}
}
private boolean isValidCoordinateId(String id) {
for (int i = 0; i < id.length(); i++) {
char c = id.charAt(i);
if (!isValidCoordinateIdCharacter(c)) {
return false;
}
}
return true;
}
private boolean isValidCoordinateIdCharacter(char c) {
return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '-' || c == '_' || c == '.';
}
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateProfileId(
String prefix,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String id,
String sourceHint,
InputLocationTracker tracker) {
if (validProfileIds.contains(id)) {
return true;
}
if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
return false;
} else {
if (!isValidProfileId(id)) {
addViolation(
problems,
severity,
version,
prefix + fieldName,
sourceHint,
"with value '" + id + "' does not match a valid profile id pattern.",
tracker);
return false;
}
validProfileIds.add(id);
return true;
}
}
private boolean isValidProfileId(String id) {
switch (id.charAt(0)) { // avoid first character that has special CLI meaning in "mvn -P xxx"
case '+': // activate
case '-': // deactivate
case '!': // deactivate
case '?': // optional
return false;
default:
}
return true;
}
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateCoordinateIdWithWildcards(
String prefix,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String id,
String sourceHint,
InputLocationTracker tracker) {
if (!validateStringNotEmpty(prefix, fieldName, problems, severity, version, id, sourceHint, tracker)) {
return false;
} else {
if (!isValidCoordinateIdWithWildCards(id)) {
addViolation(
problems,
severity,
version,
prefix + fieldName,
sourceHint,
"with value '" + id + "' does not match a valid coordinate id pattern.",
tracker);
return false;
}
return true;
}
}
private boolean isValidCoordinateIdWithWildCards(String id) {
for (int i = 0; i < id.length(); i++) {
char c = id.charAt(i);
if (!isValidCoordinateIdWithWildCardCharacter(c)) {
return false;
}
}
return true;
}
private boolean isValidCoordinateIdWithWildCardCharacter(char c) {
return isValidCoordinateIdCharacter(c) || c == '?' || c == '*';
}
private boolean validateStringNoExpression(
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
InputLocationTracker tracker) {
if (!hasExpression(string)) {
return true;
}
addViolation(
problems,
severity,
version,
fieldName,
null,
"contains an expression but should be a constant.",
tracker);
return false;
}
private boolean validateVersionNoExpression(
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
InputLocationTracker tracker) {
if (!hasExpression(string)) {
return true;
}
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;
}
}
return true;
}
private boolean hasExpression(String value) {
return value != null && value.contains("${");
}
private boolean hasProjectExpression(String value) {
return value != null && value.contains("${project.");
}
private boolean validateStringNotEmpty(
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
InputLocationTracker tracker) {
return validateStringNotEmpty(EMPTY, fieldName, problems, severity, version, string, null, tracker);
}
/**
* Asserts:
* <p/>
* <ul>
* <li><code>string != null</code>
* <li><code>string.length > 0</code>
* </ul>
*/
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateStringNotEmpty(
String prefix,
String prefix2,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
String sourceHint,
InputLocationTracker tracker) {
if (!validateNotNull(prefix, prefix2, fieldName, problems, severity, version, string, sourceHint, tracker)) {
return false;
}
if (!string.isEmpty()) {
return true;
}
addViolation(problems, severity, version, prefix + prefix2 + fieldName, sourceHint, "is missing.", tracker);
return false;
}
/**
* Asserts:
* <p/>
* <ul>
* <li><code>string != null</code>
* <li><code>string.length > 0</code>
* </ul>
*/
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateStringNotEmpty(
String prefix,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
String sourceHint,
InputLocationTracker tracker) {
if (!validateNotNull(prefix, fieldName, problems, severity, version, string, sourceHint, tracker)) {
return false;
}
if (!string.isEmpty()) {
return true;
}
addViolation(problems, severity, version, prefix + fieldName, sourceHint, "is missing.", tracker);
return false;
}
/**
* Asserts:
* <p/>
* <ul>
* <li><code>string != null</code>
* </ul>
*/
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateNotNull(
String prefix,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
Object object,
String sourceHint,
InputLocationTracker tracker) {
if (object != null) {
return true;
}
addViolation(problems, severity, version, prefix + fieldName, sourceHint, "is missing.", tracker);
return false;
}
/**
* Asserts:
* <p/>
* <ul>
* <li><code>string != null</code>
* </ul>
*/
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateNotNull(
String prefix,
String prefix2,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
Object object,
String sourceHint,
InputLocationTracker tracker) {
if (object != null) {
return true;
}
addViolation(problems, severity, version, prefix + prefix2 + fieldName, sourceHint, "is missing.", tracker);
return false;
}
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateBoolean(
String prefix,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
String sourceHint,
InputLocationTracker tracker) {
if (string == null || string.isEmpty()) {
return true;
}
if ("true".equalsIgnoreCase(string) || "false".equalsIgnoreCase(string)) {
return true;
}
addViolation(
problems,
severity,
version,
prefix + fieldName,
sourceHint,
"must be 'true' or 'false' but is '" + string + "'.",
tracker);
return false;
}
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateEnum(
String prefix,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
String sourceHint,
InputLocationTracker tracker,
String... validValues) {
if (string == null || string.isEmpty()) {
return true;
}
List<String> values = Arrays.asList(validValues);
if (values.contains(string)) {
return true;
}
addViolation(
problems,
severity,
version,
prefix + fieldName,
sourceHint,
"must be one of " + values + " but is '" + string + "'.",
tracker);
return false;
}
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateModelVersion(
ModelProblemCollector problems, String string, InputLocationTracker tracker, List<String> validVersions) {
if (string == null || string.isEmpty()) {
return true;
}
if (validVersions.contains(string)) {
return true;
}
boolean newerThanAll = true;
boolean olderThanAll = true;
for (String validValue : validVersions) {
final int comparison = compareModelVersions(validValue, string);
newerThanAll = newerThanAll && comparison < 0;
olderThanAll = olderThanAll && comparison > 0;
}
if (newerThanAll) {
addViolation(
problems,
Severity.FATAL,
Version.V20,
"modelVersion",
null,
"of '" + string + "' is newer than the versions supported by this version of Maven: "
+ validVersions + ". Building this project requires a newer version of Maven.",
tracker);
} else if (olderThanAll) {
// note this will not be hit for Maven 1.x project.xml as it is an incompatible schema
addViolation(
problems,
Severity.FATAL,
Version.V20,
"modelVersion",
null,
"of '" + string + "' is older than the versions supported by this version of Maven: "
+ validVersions + ". Building this project requires an older version of Maven.",
tracker);
} else {
addViolation(
problems,
Severity.ERROR,
Version.V20,
"modelVersion",
null,
"must be one of " + validVersions + " but is '" + string + "'.",
tracker);
}
return false;
}
/**
* Compares two model versions.
*
* @param first the first version.
* @param second the second version.
* @return negative if the first version is newer than the second version, zero if they are the same or positive if
* the second version is the newer.
*/
private static int compareModelVersions(String first, String second) {
// we use a dedicated comparator because we control our model version scheme.
String[] firstSegments = first.split("\\.");
String[] secondSegments = second.split("\\.");
for (int i = 0; i < Math.max(firstSegments.length, secondSegments.length); i++) {
int result = Long.valueOf(i < firstSegments.length ? firstSegments[i] : "0")
.compareTo(Long.valueOf(i < secondSegments.length ? secondSegments[i] : "0"));
if (result != 0) {
return result;
}
}
return 0;
}
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateBannedCharacters(
String prefix,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
String sourceHint,
InputLocationTracker tracker,
String banned) {
if (string != null) {
for (int i = string.length() - 1; i >= 0; i--) {
if (banned.indexOf(string.charAt(i)) >= 0) {
addViolation(
problems,
severity,
version,
prefix + fieldName,
sourceHint,
"must not contain any of these characters " + banned + " but found " + string.charAt(i),
tracker);
return false;
}
}
}
return true;
}
@SuppressWarnings("checkstyle:parameternumber")
private boolean validateVersion(
String prefix,
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
String sourceHint,
InputLocationTracker tracker) {
if (string == null || string.isEmpty()) {
return true;
}
if (hasExpression(string)) {
addViolation(
problems,
severity,
version,
prefix + fieldName,
sourceHint,
"must be a valid version but is '" + string + "'.",
tracker);
return false;
}
return validateBannedCharacters(
prefix, fieldName, problems, severity, version, string, sourceHint, tracker, ILLEGAL_VERSION_CHARS);
}
private boolean validate20ProperSnapshotVersion(
String fieldName,
ModelProblemCollector problems,
Severity severity,
Version version,
String string,
String sourceHint,
InputLocationTracker tracker) {
if (string == null || string.isEmpty()) {
return true;
}
if (string.endsWith("SNAPSHOT") && !string.endsWith("-SNAPSHOT")) {
addViolation(
problems,
severity,
version,
fieldName,
sourceHint,
"uses an unsupported snapshot version format, should be '*-SNAPSHOT' instead.",
tracker);
return false;
}
return true;
}
private boolean validate20PluginVersion(
String fieldName,
ModelProblemCollector problems,
String string,
String sourceHint,
InputLocationTracker tracker,
ModelBuildingRequest request) {
if (string == null) {
// NOTE: The check for missing plugin versions is handled directly by the model builder
return true;
}
Severity errOn30 = getSeverity(request, ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_0);
if (!validateVersion(EMPTY, fieldName, problems, errOn30, Version.V20, string, sourceHint, tracker)) {
return false;
}
if (string.isEmpty() || "RELEASE".equals(string) || "LATEST".equals(string)) {
addViolation(
problems,
errOn30,
Version.V20,
fieldName,
sourceHint,
"must be a valid version but is '" + string + "'.",
tracker);
return false;
}
return true;
}
private static void addViolation(
ModelProblemCollector problems,
Severity severity,
Version version,
String fieldName,
String sourceHint,
String message,
InputLocationTracker tracker) {
StringBuilder buffer = new StringBuilder(256);
buffer.append('\'').append(fieldName).append('\'');
if (sourceHint != null) {
buffer.append(" for ").append(sourceHint);
}
buffer.append(' ').append(message);
problems.add(new ModelProblemCollectorRequest(severity, version)
.setMessage(buffer.toString())
.setLocation(getLocation(fieldName, tracker)));
}
private static org.apache.maven.model.InputLocation getLocation(String fieldName, InputLocationTracker tracker) {
InputLocation location = null;
if (tracker != null) {
if (fieldName != null) {
Object key = fieldName;
int idx = fieldName.lastIndexOf('.');
if (idx >= 0) {
fieldName = fieldName.substring(idx + 1);
key = fieldName;
}
if (fieldName.endsWith("]")) {
key = fieldName.substring(fieldName.lastIndexOf('[') + 1, fieldName.length() - 1);
try {
key = Integer.valueOf(key.toString());
} catch (NumberFormatException e) {
// use key as is
}
}
location = tracker.getLocation(key);
}
if (location == null) {
location = tracker.getLocation(EMPTY);
}
}
return location != null ? new org.apache.maven.model.InputLocation(location) : null;
}
private static boolean equals(String s1, String s2) {
String c1 = s1 == null ? "" : s1.trim();
String c2 = s2 == null ? "" : s2.trim();
return c1.equals(c2);
}
private static Severity getSeverity(ModelBuildingRequest request, int errorThreshold) {
return getSeverity(request.getValidationLevel(), errorThreshold);
}
private static Severity getSeverity(int validationLevel, int errorThreshold) {
if (validationLevel < errorThreshold) {
return Severity.WARNING;
} else {
return Severity.ERROR;
}
}
}