| /* |
| * 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.artifact.repository.metadata; |
| |
| import javax.xml.stream.XMLInputFactory; |
| import javax.xml.stream.XMLOutputFactory; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.text.DateFormat; |
| import java.text.SimpleDateFormat; |
| import java.util.Date; |
| import java.util.GregorianCalendar; |
| import java.util.TimeZone; |
| |
| import com.ctc.wstx.stax.WstxInputFactory; |
| import com.ctc.wstx.stax.WstxOutputFactory; |
| import org.apache.maven.artifact.repository.metadata.io.MetadataStaxReader; |
| import org.apache.maven.artifact.repository.metadata.io.MetadataStaxWriter; |
| import org.eclipse.aether.artifact.Artifact; |
| import org.eclipse.aether.artifact.DefaultArtifact; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertFalse; |
| import static org.junit.jupiter.api.Assertions.assertNotNull; |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
| |
| class MetadataTest { |
| |
| Artifact artifact; |
| |
| Metadata target; |
| |
| @BeforeEach |
| void before() { |
| artifact = new DefaultArtifact("myGroup:myArtifact:1.0-SNAPSHOT"); |
| target = createMetadataFromArtifact(artifact); |
| } |
| |
| /*--- START test common metadata ---*/ |
| @Test |
| void mergeEmptyMetadata() throws Exception { |
| Metadata metadata = new Metadata(); |
| assertFalse(metadata.merge(new Metadata())); |
| } |
| |
| @Test |
| void mergeDifferentGAV() throws Exception { |
| // merge implicitly assumes that merge is only called on the same GAV and does not perform any validation here! |
| Metadata source = new Metadata(); |
| source.setArtifactId("source-artifact"); |
| source.setGroupId("source-group"); |
| source.setVersion("2.0"); |
| assertFalse(target.merge(source)); |
| assertEquals("myArtifact", target.getArtifactId()); |
| assertEquals("myGroup", target.getGroupId()); |
| assertEquals("1.0-SNAPSHOT", target.getVersion()); |
| } |
| /*--- END test common metadata ---*/ |
| |
| /*--- START test "groupId/artifactId/version" metadata ---*/ |
| @Test |
| void mergeSnapshotWithEmptyList() throws Exception { |
| Snapshot snapshot = new Snapshot(); |
| snapshot.setBuildNumber(3); |
| snapshot.setTimestamp("20200710.072412"); |
| target.getVersioning().setSnapshot(snapshot); |
| target.getVersioning().setLastUpdated("20200921071745"); |
| SnapshotVersion sv = new SnapshotVersion(); |
| sv.setClassifier("sources"); |
| sv.setExtension("jar"); |
| sv.setUpdated("20200710072412"); |
| target.getVersioning().addSnapshotVersion(sv); |
| |
| Metadata source = createMetadataFromArtifact(artifact); |
| // nothing should be actually changed, but still merge returns true |
| assertTrue(target.merge(source)); |
| |
| // NOTE! Merge updates last updated to source |
| assertEquals("20200921071745", source.getVersioning().getLastUpdated()); |
| |
| assertEquals("myArtifact", target.getArtifactId()); |
| assertEquals("myGroup", target.getGroupId()); |
| |
| assertEquals(3, target.getVersioning().getSnapshot().getBuildNumber()); |
| assertEquals("20200710.072412", target.getVersioning().getSnapshot().getTimestamp()); |
| |
| assertEquals(1, target.getVersioning().getSnapshotVersions().size()); |
| assertEquals( |
| "sources", target.getVersioning().getSnapshotVersions().get(0).getClassifier()); |
| assertEquals("jar", target.getVersioning().getSnapshotVersions().get(0).getExtension()); |
| assertEquals( |
| "20200710072412", |
| target.getVersioning().getSnapshotVersions().get(0).getUpdated()); |
| } |
| |
| @Test |
| void mergeWithSameSnapshotWithDifferentVersionsAndNewerLastUpdated() { |
| Metadata source = createMetadataFromArtifact(artifact); |
| Date before = new Date(System.currentTimeMillis() - 5000); |
| Date after = new Date(System.currentTimeMillis()); |
| addSnapshotVersion(target.getVersioning(), "jar", before, "1", 1); |
| SnapshotVersion sv2 = |
| addSnapshotVersion(source.getVersioning(), "jar", after, "1.0-" + formatDate(after, true) + "-2", 2); |
| SnapshotVersion sv3 = |
| addSnapshotVersion(source.getVersioning(), "pom", after, "1.0-" + formatDate(after, true) + "-2", 2); |
| assertTrue(target.merge(source)); |
| Versioning actualVersioning = target.getVersioning(); |
| assertEquals(2, actualVersioning.getSnapshotVersions().size()); |
| assertEquals(sv2, actualVersioning.getSnapshotVersions().get(0)); |
| assertEquals(sv3, actualVersioning.getSnapshotVersions().get(1)); |
| assertEquals(formatDate(after, false), actualVersioning.getLastUpdated()); |
| assertEquals(formatDate(after, true), actualVersioning.getSnapshot().getTimestamp()); |
| assertEquals(2, actualVersioning.getSnapshot().getBuildNumber()); |
| } |
| |
| @Test |
| void mergeWithSameSnapshotWithDifferentVersionsAndOlderLastUpdated() { |
| Metadata source = createMetadataFromArtifact(artifact); |
| Date before = new Date(System.currentTimeMillis() - 5000); |
| Date after = new Date(System.currentTimeMillis()); |
| SnapshotVersion sv1 = addSnapshotVersion(target.getVersioning(), after, artifact); |
| addSnapshotVersion(source.getVersioning(), before, artifact); |
| // nothing should be updated, as the target was already updated at a later date than source |
| assertFalse(target.merge(source)); |
| assertEquals(1, target.getVersioning().getSnapshotVersions().size()); |
| assertEquals(sv1, target.getVersioning().getSnapshotVersions().get(0)); |
| assertEquals(formatDate(after, false), target.getVersioning().getLastUpdated()); |
| assertEquals( |
| formatDate(after, true), target.getVersioning().getSnapshot().getTimestamp()); |
| } |
| |
| @Test |
| void mergeWithSameSnapshotWithSameVersionAndTimestamp() { |
| Metadata source = createMetadataFromArtifact(artifact); |
| Date date = new Date(); |
| addSnapshotVersion(target.getVersioning(), date, artifact); |
| SnapshotVersion sv1 = addSnapshotVersion(source.getVersioning(), date, artifact); |
| // although nothing has changed merge returns true, as the last modified date is equal |
| // TODO: improve merge here? |
| assertTrue(target.merge(source)); |
| assertEquals(1, target.getVersioning().getSnapshotVersions().size()); |
| assertEquals(sv1, target.getVersioning().getSnapshotVersions().get(0)); |
| assertEquals(formatDate(date, false), target.getVersioning().getLastUpdated()); |
| assertEquals( |
| formatDate(date, true), target.getVersioning().getSnapshot().getTimestamp()); |
| } |
| |
| @Test |
| void mergeLegacyWithSnapshotLegacy() { |
| Metadata source = createMetadataFromArtifact(artifact); |
| Date before = new Date(System.currentTimeMillis() - 5000); |
| Date after = new Date(System.currentTimeMillis()); |
| // legacy metadata did not have "versioning.snapshotVersions" |
| addSnapshotVersionLegacy(target.getVersioning(), before, 1); |
| addSnapshotVersionLegacy(source.getVersioning(), after, 2); |
| // although nothing has changed merge returns true, as the last modified date is equal |
| // TODO: improve merge here? |
| assertTrue(target.merge(source)); |
| assertEquals(0, target.getVersioning().getSnapshotVersions().size()); |
| assertEquals(formatDate(after, false), target.getVersioning().getLastUpdated()); |
| assertEquals( |
| formatDate(after, true), target.getVersioning().getSnapshot().getTimestamp()); |
| } |
| |
| @Test |
| void mergeLegacyWithSnapshot() { |
| Metadata source = createMetadataFromArtifact(artifact); |
| Date before = new Date(System.currentTimeMillis() - 5000); |
| Date after = new Date(System.currentTimeMillis()); |
| // legacy metadata did not have "versioning.snapshotVersions" |
| addSnapshotVersionLegacy(target.getVersioning(), before, 1); |
| addSnapshotVersion(source.getVersioning(), after, artifact); |
| // although nothing has changed merge returns true, as the last modified date is equal |
| // TODO: improve merge here? |
| assertTrue(target.merge(source)); |
| // never convert from legacy format to v1.1 format |
| assertEquals(0, target.getVersioning().getSnapshotVersions().size()); |
| assertEquals(formatDate(after, false), target.getVersioning().getLastUpdated()); |
| assertEquals( |
| formatDate(after, true), target.getVersioning().getSnapshot().getTimestamp()); |
| } |
| |
| @Test |
| void mergeWithSnapshotLegacy() { |
| Metadata source = createMetadataFromArtifact(artifact); |
| Date before = new Date(System.currentTimeMillis() - 5000); |
| Date after = new Date(System.currentTimeMillis()); |
| addSnapshotVersion(target.getVersioning(), before, artifact); |
| // legacy metadata did not have "versioning.snapshotVersions" |
| addSnapshotVersionLegacy(source.getVersioning(), after, 2); |
| // although nothing has changed merge returns true, as the last modified date is equal |
| // TODO: improve merge here? |
| assertTrue(target.merge(source)); |
| // the result must be legacy format as well |
| assertEquals(0, target.getVersioning().getSnapshotVersions().size()); |
| assertEquals(formatDate(after, false), target.getVersioning().getLastUpdated()); |
| assertEquals( |
| formatDate(after, true), target.getVersioning().getSnapshot().getTimestamp()); |
| assertEquals(2, target.getVersioning().getSnapshot().getBuildNumber()); |
| } |
| /*-- END test "groupId/artifactId/version" metadata ---*/ |
| |
| @Test |
| void testRoundtrip() throws Exception { |
| System.setProperty(XMLInputFactory.class.getName(), WstxInputFactory.class.getName()); |
| System.setProperty(XMLOutputFactory.class.getName(), WstxOutputFactory.class.getName()); |
| |
| Metadata source = new Metadata(org.apache.maven.artifact.repository.metadata.v4.Metadata.newBuilder( |
| createMetadataFromArtifact(artifact).getDelegate(), true) |
| .modelEncoding("UTF-16") |
| .build()); |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| new MetadataStaxWriter().write(baos, source.getDelegate()); |
| Metadata source2 = |
| new Metadata(new MetadataStaxReader().read(new ByteArrayInputStream(baos.toByteArray()), true)); |
| assertNotNull(source2); |
| } |
| |
| /*-- START helper methods to populate metadata objects ---*/ |
| private static final String SNAPSHOT = "SNAPSHOT"; |
| |
| private static final String DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT = "yyyyMMdd.HHmmss"; |
| |
| private static final String DEFAULT_DATE_FORMAT = "yyyyMMddHHmmss"; |
| |
| private static String formatDate(Date date, boolean forSnapshotTimestamp) { |
| // logic from metadata.mdo, class "Versioning" |
| TimeZone timezone = TimeZone.getTimeZone("UTC"); |
| DateFormat fmt = |
| new SimpleDateFormat(forSnapshotTimestamp ? DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT : DEFAULT_DATE_FORMAT); |
| fmt.setCalendar(new GregorianCalendar()); |
| fmt.setTimeZone(timezone); |
| return fmt.format(date); |
| } |
| |
| private static Metadata createMetadataFromArtifact(Artifact artifact) { |
| Metadata metadata = new Metadata(); |
| metadata.setArtifactId(artifact.getArtifactId()); |
| metadata.setGroupId(artifact.getGroupId()); |
| metadata.setVersion(artifact.getVersion()); |
| metadata.setVersioning(new Versioning()); |
| return metadata; |
| } |
| |
| private static SnapshotVersion addSnapshotVersion(Versioning versioning, Date timestamp, Artifact artifact) { |
| int buildNumber = 1; |
| // this generates timestamped versions like maven-resolver-provider: |
| // https://github.com/apache/maven/blob/03df5f7c639db744a3597c7175c92c8e2a27767b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RemoteSnapshotMetadata.java#L79 |
| String version = artifact.getVersion(); |
| String qualifier = formatDate(timestamp, true) + '-' + buildNumber; |
| version = version.substring(0, version.length() - SNAPSHOT.length()) + qualifier; |
| return addSnapshotVersion(versioning, artifact.getExtension(), timestamp, version, buildNumber); |
| } |
| |
| private static SnapshotVersion addSnapshotVersion( |
| Versioning versioning, String extension, Date timestamp, String version, int buildNumber) { |
| Snapshot snapshot = new Snapshot(); |
| snapshot.setBuildNumber(buildNumber); |
| snapshot.setTimestamp(formatDate(timestamp, true)); |
| |
| SnapshotVersion sv = new SnapshotVersion(); |
| sv.setExtension(extension); |
| sv.setVersion(version); |
| sv.setUpdated(formatDate(timestamp, false)); |
| versioning.addSnapshotVersion(sv); |
| |
| // make the new snapshot the current one |
| versioning.setSnapshot(snapshot); |
| versioning.setLastUpdatedTimestamp(timestamp); |
| return sv; |
| } |
| |
| // the format written by Maven 2 |
| // (https://maven.apache.org/ref/2.2.1/maven-repository-metadata/repository-metadata.html) |
| private static void addSnapshotVersionLegacy(Versioning versioning, Date timestamp, int buildNumber) { |
| Snapshot snapshot = new Snapshot(); |
| snapshot.setBuildNumber(buildNumber); |
| snapshot.setTimestamp(formatDate(timestamp, true)); |
| |
| versioning.setSnapshot(snapshot); |
| versioning.setLastUpdatedTimestamp(timestamp); |
| } |
| /*-- END helper methods to populate metadata objects ---*/ |
| } |