diff --git a/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java b/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java index 73629bc2ea..f82800bd4d 100644 --- a/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java +++ b/maven-core/src/test/java/org/apache/maven/project/DefaultMavenProjectBuilderTest.java @@ -19,15 +19,15 @@ package org.apache.maven.project; * under the License. */ -import java.io.File; -import java.util.ArrayList; -import java.util.List; - import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; import org.codehaus.plexus.util.FileUtils; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + public class DefaultMavenProjectBuilderTest extends AbstractMavenProjectTestCase { @@ -112,6 +112,66 @@ public class DefaultMavenProjectBuilderTest assertEquals( "first", project.getBuildPlugins().get( 0 ).getExecutions().get( 0 ).getId() ); } + public void testFutureModelVersion() + throws Exception + { + File f1 = getTestFile( "src/test/resources/projects/future-model-version-pom.xml" ); + + try + { + getProject( f1 ); + fail( "Expected to fail for future versions" ); + } + catch ( ProjectBuildingException e ) + { + assertContains( "Building this project requires a newer version of Maven", e.getMessage() ); + } + } + + public void testPastModelVersion() + throws Exception + { + // a Maven 1.x pom will not even + // update the resource if we stop supporting modelVersion 4.0.0 + File f1 = getTestFile( "src/test/resources/projects/past-model-version-pom.xml" ); + + try + { + getProject( f1 ); + fail( "Expected to fail for past versions" ); + } + catch ( ProjectBuildingException e ) + { + assertContains( "Building this project requires an older version of Maven", e.getMessage() ); + } + } + + public void testFutureSchemaModelVersion() + throws Exception + { + File f1 = getTestFile( "src/test/resources/projects/future-schema-model-version-pom.xml" ); + + try + { + getProject( f1 ); + fail( "Expected to fail for future versions" ); + } + catch ( ProjectBuildingException e ) + { + assertContains( "Building this project requires a newer version of Maven", e.getMessage() ); + } + } + + private void assertContains( String expected, String actual ) + { + if ( actual == null || !actual.contains( expected ) ) + { + fail( "Expected: a string containing " + expected + "\nActual: " + ( actual == null + ? "null" + : "'" + actual + "'" ) ); + } + } + public void testBuildStubModelForMissingRemotePom() throws Exception { diff --git a/maven-core/src/test/resources/projects/future-model-version-pom.xml b/maven-core/src/test/resources/projects/future-model-version-pom.xml new file mode 100644 index 0000000000..1a73a44434 --- /dev/null +++ b/maven-core/src/test/resources/projects/future-model-version-pom.xml @@ -0,0 +1,25 @@ + + + + 4.0.1 + tests.project + future-model-version + 1 + diff --git a/maven-core/src/test/resources/projects/future-schema-model-version-pom.xml b/maven-core/src/test/resources/projects/future-schema-model-version-pom.xml new file mode 100644 index 0000000000..f234aab7df --- /dev/null +++ b/maven-core/src/test/resources/projects/future-schema-model-version-pom.xml @@ -0,0 +1,24 @@ + + + + 4.999.999 + + tests.project:future-model-version + diff --git a/maven-core/src/test/resources/projects/past-model-version-pom.xml b/maven-core/src/test/resources/projects/past-model-version-pom.xml new file mode 100644 index 0000000000..cdc7f85cd1 --- /dev/null +++ b/maven-core/src/test/resources/projects/past-model-version-pom.xml @@ -0,0 +1,26 @@ + + + + + 3.9.9 + tests.project + past-model-version + 1 + diff --git a/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java b/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java index 749dd2f69c..722c8c2b56 100644 --- a/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java +++ b/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java @@ -19,16 +19,6 @@ package org.apache.maven.model.validation; * under the License. */ -import java.io.File; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - import org.apache.maven.model.Activation; import org.apache.maven.model.ActivationFile; import org.apache.maven.model.Build; @@ -58,6 +48,16 @@ import org.apache.maven.model.interpolation.AbstractStringBasedModelInterpolator import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.util.StringUtils; +import java.io.File; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * @author Trygve Laugstøl */ @@ -122,8 +122,7 @@ public class DefaultModelValidator // models without a version starting with 3.4. validateStringNotEmpty( "modelVersion", problems, Severity.ERROR, Version.V20, m.getModelVersion(), m ); - validateEnum( "modelVersion", problems, Severity.ERROR, Version.V20, m.getModelVersion(), null, m, - "4.0.0" ); + validateModelVersion( problems, m.getModelVersion(), m, "4.0.0" ); validateStringNoExpression( "groupId", problems, Severity.WARNING, Version.V20, m.getGroupId(), m ); if ( parent == null ) @@ -1041,6 +1040,83 @@ public class DefaultModelValidator return false; } + @SuppressWarnings( "checkstyle:parameternumber" ) + private boolean validateModelVersion( ModelProblemCollector problems, String string, InputLocationTracker tracker, + String... validVersions ) + { + if ( string == null || string.length() <= 0 ) + { + return true; + } + + List values = Arrays.asList( validVersions ); + + if ( values.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: " + values + + ". 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: " + values + + ". Building this project requires an older version of Maven.", tracker ); + + } + else + { + addViolation( problems, Severity.ERROR, Version.V20, "modelVersion", null, + "must be one of " + values + " 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 = StringUtils.split( first, "." ); + String[] secondSegments = StringUtils.split( second, "." ); + for ( int i = 0; i < Math.min( firstSegments.length, secondSegments.length ); i++ ) + { + int result = Long.valueOf( firstSegments[i] ).compareTo( Long.valueOf( secondSegments[i] ) ); + if ( result != 0 ) + { + return result; + } + } + if ( firstSegments.length == secondSegments.length ) + { + return 0; + } + return firstSegments.length > secondSegments.length ? -1 : 1; + } + @SuppressWarnings( "checkstyle:parameternumber" ) private boolean validateBannedCharacters( String fieldName, ModelProblemCollector problems, Severity severity, Version version, String string, String sourceHint, diff --git a/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java b/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java index a9d4c00e39..9d5f172cbb 100644 --- a/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java +++ b/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java @@ -129,9 +129,9 @@ public class DefaultModelValidatorTest SimpleProblemCollector result = validateRaw( "bad-modelVersion.xml", ModelBuildingRequest.VALIDATION_LEVEL_STRICT ); - assertViolations( result, 0, 1, 0 ); + assertViolations( result, 1, 0, 0 ); - assertTrue( result.getErrors().get( 0 ).contains( "modelVersion" ) ); + assertTrue( result.getFatals().get( 0 ).contains( "modelVersion" ) ); } public void testMissingArtifactId()