View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.artifact.repository.metadata;
20  
21  import java.text.DateFormat;
22  import java.text.SimpleDateFormat;
23  import java.util.Date;
24  import java.util.GregorianCalendar;
25  import java.util.TimeZone;
26  
27  import org.eclipse.aether.artifact.Artifact;
28  import org.eclipse.aether.artifact.DefaultArtifact;
29  import org.junit.Before;
30  import org.junit.Test;
31  
32  import static org.junit.Assert.assertEquals;
33  import static org.junit.Assert.assertFalse;
34  import static org.junit.Assert.assertTrue;
35  
36  public class MetadataTest {
37  
38      Artifact artifact;
39  
40      Metadata target;
41  
42      @Before
43      public void before() {
44          artifact = new DefaultArtifact("myGroup:myArtifact:1.0-SNAPSHOT");
45          target = createMetadataFromArtifact(artifact);
46      }
47  
48      /*--- START test common metadata ---*/
49      @Test
50      public void mergeEmptyMetadata() throws Exception {
51          Metadata metadata = new Metadata();
52          assertFalse(metadata.merge(new Metadata()));
53      }
54  
55      @Test
56      public void mergeDifferentGAV() throws Exception {
57          // merge implicitly assumes that merge is only called on the same GAV and does not perform any validation here!
58          Metadata source = new Metadata();
59          source.setArtifactId("source-artifact");
60          source.setGroupId("source-group");
61          source.setVersion("2.0");
62          assertFalse(target.merge(source));
63          assertEquals("myArtifact", target.getArtifactId());
64          assertEquals("myGroup", target.getGroupId());
65          assertEquals("1.0-SNAPSHOT", target.getVersion());
66      }
67      /*--- END test common metadata ---*/
68  
69      /*--- START test "groupId/artifactId/version" metadata ---*/
70      @Test
71      public void mergeSnapshotWithEmptyList() throws Exception {
72          Snapshot snapshot = new Snapshot();
73          snapshot.setBuildNumber(3);
74          snapshot.setTimestamp("20200710.072412");
75          target.getVersioning().setSnapshot(snapshot);
76          target.getVersioning().setLastUpdated("20200921071745");
77          SnapshotVersion sv = new SnapshotVersion();
78          sv.setClassifier("sources");
79          sv.setExtension("jar");
80          sv.setUpdated("20200710072412");
81          target.getVersioning().addSnapshotVersion(sv);
82  
83          Metadata source = createMetadataFromArtifact(artifact);
84          // nothing should be actually changed, but still merge returns true
85          assertTrue(target.merge(source));
86  
87          // NOTE! Merge updates last updated to source
88          assertEquals("20200921071745", source.getVersioning().getLastUpdated());
89  
90          assertEquals("myArtifact", target.getArtifactId());
91          assertEquals("myGroup", target.getGroupId());
92  
93          assertEquals(3, target.getVersioning().getSnapshot().getBuildNumber());
94          assertEquals("20200710.072412", target.getVersioning().getSnapshot().getTimestamp());
95  
96          assertEquals(1, target.getVersioning().getSnapshotVersions().size());
97          assertEquals(
98                  "sources", target.getVersioning().getSnapshotVersions().get(0).getClassifier());
99          assertEquals("jar", target.getVersioning().getSnapshotVersions().get(0).getExtension());
100         assertEquals(
101                 "20200710072412",
102                 target.getVersioning().getSnapshotVersions().get(0).getUpdated());
103     }
104 
105     @Test
106     public void mergeWithSameSnapshotWithDifferentVersionsAndNewerLastUpdated() {
107         Metadata source = createMetadataFromArtifact(artifact);
108         Date before = new Date(System.currentTimeMillis() - 5000);
109         Date after = new Date(System.currentTimeMillis());
110         addSnapshotVersion(target.getVersioning(), "jar", before, "1", 1);
111         SnapshotVersion sv2 =
112                 addSnapshotVersion(source.getVersioning(), "jar", after, "1.0-" + formatDate(after, true) + "-2", 2);
113         SnapshotVersion sv3 =
114                 addSnapshotVersion(source.getVersioning(), "pom", after, "1.0-" + formatDate(after, true) + "-2", 2);
115         assertTrue(target.merge(source));
116         Versioning actualVersioning = target.getVersioning();
117         assertEquals(2, actualVersioning.getSnapshotVersions().size());
118         assertEquals(sv2, actualVersioning.getSnapshotVersions().get(0));
119         assertEquals(sv3, actualVersioning.getSnapshotVersions().get(1));
120         assertEquals(formatDate(after, false), actualVersioning.getLastUpdated());
121         assertEquals(formatDate(after, true), actualVersioning.getSnapshot().getTimestamp());
122         assertEquals(2, actualVersioning.getSnapshot().getBuildNumber());
123     }
124 
125     @Test
126     public void mergeWithSameSnapshotWithDifferentVersionsAndOlderLastUpdated() {
127         Metadata source = createMetadataFromArtifact(artifact);
128         Date before = new Date(System.currentTimeMillis() - 5000);
129         Date after = new Date(System.currentTimeMillis());
130         SnapshotVersion sv1 = addSnapshotVersion(target.getVersioning(), after, artifact);
131         addSnapshotVersion(source.getVersioning(), before, artifact);
132         // nothing should be updated, as the target was already updated at a later date than source
133         assertFalse(target.merge(source));
134         assertEquals(1, target.getVersioning().getSnapshotVersions().size());
135         assertEquals(sv1, target.getVersioning().getSnapshotVersions().get(0));
136         assertEquals(formatDate(after, false), target.getVersioning().getLastUpdated());
137         assertEquals(
138                 formatDate(after, true), target.getVersioning().getSnapshot().getTimestamp());
139     }
140 
141     @Test
142     public void mergeWithSameSnapshotWithSameVersionAndTimestamp() {
143         Metadata source = createMetadataFromArtifact(artifact);
144         Date date = new Date();
145         addSnapshotVersion(target.getVersioning(), date, artifact);
146         SnapshotVersion sv1 = addSnapshotVersion(source.getVersioning(), date, artifact);
147         // although nothing has changed merge returns true, as the last modified date is equal
148         // TODO: improve merge here?
149         assertTrue(target.merge(source));
150         assertEquals(1, target.getVersioning().getSnapshotVersions().size());
151         assertEquals(sv1, target.getVersioning().getSnapshotVersions().get(0));
152         assertEquals(formatDate(date, false), target.getVersioning().getLastUpdated());
153         assertEquals(
154                 formatDate(date, true), target.getVersioning().getSnapshot().getTimestamp());
155     }
156 
157     @Test
158     public void mergeLegacyWithSnapshotLegacy() {
159         Metadata source = createMetadataFromArtifact(artifact);
160         Date before = new Date(System.currentTimeMillis() - 5000);
161         Date after = new Date(System.currentTimeMillis());
162         // legacy metadata did not have "versioning.snapshotVersions"
163         addSnapshotVersionLegacy(target.getVersioning(), before, 1);
164         addSnapshotVersionLegacy(source.getVersioning(), after, 2);
165         // although nothing has changed merge returns true, as the last modified date is equal
166         // TODO: improve merge here?
167         assertTrue(target.merge(source));
168         assertEquals(0, target.getVersioning().getSnapshotVersions().size());
169         assertEquals(formatDate(after, false), target.getVersioning().getLastUpdated());
170         assertEquals(
171                 formatDate(after, true), target.getVersioning().getSnapshot().getTimestamp());
172     }
173 
174     @Test
175     public void mergeLegacyWithSnapshot() {
176         Metadata source = createMetadataFromArtifact(artifact);
177         Date before = new Date(System.currentTimeMillis() - 5000);
178         Date after = new Date(System.currentTimeMillis());
179         // legacy metadata did not have "versioning.snapshotVersions"
180         addSnapshotVersionLegacy(target.getVersioning(), before, 1);
181         addSnapshotVersion(source.getVersioning(), after, artifact);
182         // although nothing has changed merge returns true, as the last modified date is equal
183         // TODO: improve merge here?
184         assertTrue(target.merge(source));
185         // never convert from legacy format to v1.1 format
186         assertEquals(0, target.getVersioning().getSnapshotVersions().size());
187         assertEquals(formatDate(after, false), target.getVersioning().getLastUpdated());
188         assertEquals(
189                 formatDate(after, true), target.getVersioning().getSnapshot().getTimestamp());
190     }
191 
192     @Test
193     public void mergeWithSnapshotLegacy() {
194         Metadata source = createMetadataFromArtifact(artifact);
195         Date before = new Date(System.currentTimeMillis() - 5000);
196         Date after = new Date(System.currentTimeMillis());
197         addSnapshotVersion(target.getVersioning(), before, artifact);
198         // legacy metadata did not have "versioning.snapshotVersions"
199         addSnapshotVersionLegacy(source.getVersioning(), after, 2);
200         // although nothing has changed merge returns true, as the last modified date is equal
201         // TODO: improve merge here?
202         assertTrue(target.merge(source));
203         // the result must be legacy format as well
204         assertEquals(0, target.getVersioning().getSnapshotVersions().size());
205         assertEquals(formatDate(after, false), target.getVersioning().getLastUpdated());
206         assertEquals(
207                 formatDate(after, true), target.getVersioning().getSnapshot().getTimestamp());
208         assertEquals(2, target.getVersioning().getSnapshot().getBuildNumber());
209     }
210     /*-- END test "groupId/artifactId/version" metadata ---*/
211 
212     /*-- START helper methods to populate metadata objects ---*/
213     private static final String SNAPSHOT = "SNAPSHOT";
214 
215     private static final String DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT = "yyyyMMdd.HHmmss";
216 
217     private static final String DEFAULT_DATE_FORMAT = "yyyyMMddHHmmss";
218 
219     private static String formatDate(Date date, boolean forSnapshotTimestamp) {
220         // logic from metadata.mdo, class "Versioning"
221         TimeZone timezone = TimeZone.getTimeZone("UTC");
222         DateFormat fmt =
223                 new SimpleDateFormat(forSnapshotTimestamp ? DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT : DEFAULT_DATE_FORMAT);
224         fmt.setCalendar(new GregorianCalendar());
225         fmt.setTimeZone(timezone);
226         return fmt.format(date);
227     }
228 
229     private static Metadata createMetadataFromArtifact(Artifact artifact) {
230         Metadata metadata = new Metadata();
231         metadata.setArtifactId(artifact.getArtifactId());
232         metadata.setGroupId(artifact.getGroupId());
233         metadata.setVersion(artifact.getVersion());
234         metadata.setVersioning(new Versioning());
235         return metadata;
236     }
237 
238     private static SnapshotVersion addSnapshotVersion(Versioning versioning, Date timestamp, Artifact artifact) {
239         int buildNumber = 1;
240         // this generates timestamped versions like maven-resolver-provider:
241         // https://github.com/apache/maven/blob/03df5f7c639db744a3597c7175c92c8e2a27767b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RemoteSnapshotMetadata.java#L79
242         String version = artifact.getVersion();
243         String qualifier = formatDate(timestamp, true) + '-' + buildNumber;
244         version = version.substring(0, version.length() - SNAPSHOT.length()) + qualifier;
245         return addSnapshotVersion(versioning, artifact.getExtension(), timestamp, version, buildNumber);
246     }
247 
248     private static SnapshotVersion addSnapshotVersion(
249             Versioning versioning, String extension, Date timestamp, String version, int buildNumber) {
250         Snapshot snapshot = new Snapshot();
251         snapshot.setBuildNumber(buildNumber);
252         snapshot.setTimestamp(formatDate(timestamp, true));
253 
254         SnapshotVersion sv = new SnapshotVersion();
255         sv.setExtension(extension);
256         sv.setVersion(version);
257         sv.setUpdated(formatDate(timestamp, false));
258         versioning.addSnapshotVersion(sv);
259 
260         // make the new snapshot the current one
261         versioning.setSnapshot(snapshot);
262         versioning.setLastUpdatedTimestamp(timestamp);
263         return sv;
264     }
265 
266     // the format written by Maven 2
267     // (https://maven.apache.org/ref/2.2.1/maven-repository-metadata/repository-metadata.html)
268     private static void addSnapshotVersionLegacy(Versioning versioning, Date timestamp, int buildNumber) {
269         Snapshot snapshot = new Snapshot();
270         snapshot.setBuildNumber(buildNumber);
271         snapshot.setTimestamp(formatDate(timestamp, true));
272 
273         versioning.setSnapshot(snapshot);
274         versioning.setLastUpdatedTimestamp(timestamp);
275     }
276     /*-- END helper methods to populate metadata objects ---*/
277 }