Skip to content

Commit 51a16e1

Browse files
authored
#687: prevent checksum recheck (#688)
1 parent a70b82c commit 51a16e1

File tree

5 files changed

+164
-71
lines changed

5 files changed

+164
-71
lines changed

cli/src/main/java/com/devonfw/tools/ide/url/model/file/json/UrlStatusState.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,17 @@ public final class UrlStatusState {
2121
*/
2222
public UrlStatusState() {
2323

24-
this.timestamp = Instant.now();
24+
this(Instant.now());
25+
}
26+
27+
/**
28+
* The constructor.
29+
*
30+
* @param timestamp the {@link #getTimestamp() timestamp}.
31+
*/
32+
public UrlStatusState(Instant timestamp) {
33+
34+
this.timestamp = timestamp;
2535
}
2636

2737
/**
@@ -78,4 +88,4 @@ public String toString() {
7888
return getClass().getSimpleName() + "@" + this.timestamp
7989
+ ((this.message == null || this.message.isEmpty()) ? "" : ":" + this.message);
8090
}
81-
}
91+
}

cli/src/main/java/com/devonfw/tools/ide/url/updater/AbstractUrlUpdater.java

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ protected HttpResponse<InputStream> doGetResponseAsStream(String url) {
173173
*
174174
* @param urlVersion the {@link UrlVersion} with the {@link UrlVersion#getName() version-number} to process.
175175
* @param downloadUrl the URL of the download for the tool.
176-
* @return true if the version was successfully updated, false otherwise.
176+
* @return {@code true} if the version was successfully added, {@code false} otherwise.
177177
*/
178178
protected boolean doAddVersion(UrlVersion urlVersion, String downloadUrl) {
179179

@@ -186,7 +186,7 @@ protected boolean doAddVersion(UrlVersion urlVersion, String downloadUrl) {
186186
* @param edition the edition of the tool.
187187
* @param urlVersion the {@link UrlVersion} with the {@link UrlVersion#getName() version-number} to process.
188188
* @param downloadUrl the URL of the download for the tool.
189-
* @return true if the version was successfully updated, false otherwise.
189+
* @return {@code true} if the version was successfully added, {@code false} otherwise.
190190
*/
191191
protected boolean doAddVersion(String edition, UrlVersion urlVersion, String downloadUrl) {
192192

@@ -200,7 +200,7 @@ protected boolean doAddVersion(String edition, UrlVersion urlVersion, String dow
200200
* @param urlVersion the {@link UrlVersion} with the {@link UrlVersion#getName() version-number} to process.
201201
* @param downloadUrl the URL of the download for the tool.
202202
* @param os the {@link OperatingSystem} for the tool (can be null).
203-
* @return true if the version was successfully updated, false otherwise.
203+
* @return {@code true} if the version was successfully added, {@code false} otherwise.
204204
*/
205205
protected boolean doAddVersion(UrlVersion urlVersion, String downloadUrl, OperatingSystem os) {
206206

@@ -214,7 +214,7 @@ protected boolean doAddVersion(UrlVersion urlVersion, String downloadUrl, Operat
214214
* @param urlVersion the {@link UrlVersion} with the {@link UrlVersion#getName() version-number} to process.
215215
* @param downloadUrl the URL of the download for the tool.
216216
* @param os the {@link OperatingSystem} for the tool (can be null).
217-
* @return true if the version was successfully updated, false otherwise.
217+
* @return {@code true} if the version was successfully added, {@code false} otherwise.
218218
*/
219219
protected boolean doAddVersion(String edition, UrlVersion urlVersion, String downloadUrl, OperatingSystem os) {
220220

@@ -228,7 +228,7 @@ protected boolean doAddVersion(String edition, UrlVersion urlVersion, String dow
228228
* @param downloadUrl the URL of the download for the tool.
229229
* @param os the {@link OperatingSystem} for the tool (can be null).
230230
* @param architecture the optional {@link SystemArchitecture}.
231-
* @return true if the version was successfully updated, false otherwise.
231+
* @return {@code true} if the version was successfully added, {@code false} otherwise.
232232
*/
233233
protected boolean doAddVersion(UrlVersion urlVersion, String downloadUrl, OperatingSystem os,
234234
SystemArchitecture architecture) {
@@ -244,7 +244,7 @@ protected boolean doAddVersion(UrlVersion urlVersion, String downloadUrl, Operat
244244
* @param downloadUrl the URL of the download for the tool.
245245
* @param os the {@link OperatingSystem} for the tool (can be null).
246246
* @param architecture the optional {@link SystemArchitecture}.
247-
* @return true if the version was successfully updated, false otherwise.
247+
* @return {@code true} if the version was successfully added, {@code false} otherwise.
248248
*/
249249
protected boolean doAddVersion(String edition, UrlVersion urlVersion, String downloadUrl, OperatingSystem os,
250250
SystemArchitecture architecture) {
@@ -261,7 +261,7 @@ protected boolean doAddVersion(String edition, UrlVersion urlVersion, String dow
261261
* @param os the optional {@link OperatingSystem}.
262262
* @param architecture the optional {@link SystemArchitecture}.
263263
* @param checksum the existing checksum (e.g. from JSON metadata) or the empty {@link String} if not available and computation needed.
264-
* @return {@code true} if the version was successfully updated, {@code false} otherwise.
264+
* @return {@code true} if the version was successfully added, {@code false} otherwise.
265265
*/
266266
protected boolean doAddVersion(UrlVersion urlVersion, String url, OperatingSystem os, SystemArchitecture architecture, String checksum) {
267267
return doAddVersion(getEdition(), urlVersion, url, os, architecture, checksum);
@@ -276,7 +276,7 @@ protected boolean doAddVersion(UrlVersion urlVersion, String url, OperatingSyste
276276
* @param os the optional {@link OperatingSystem}.
277277
* @param architecture the optional {@link SystemArchitecture}.
278278
* @param checksum the existing checksum (e.g. from JSON metadata) or the empty {@link String} if not available and computation needed.
279-
* @return {@code true} if the version was successfully updated, {@code false} otherwise.
279+
* @return {@code true} if the version was successfully added, {@code false} otherwise.
280280
*/
281281
protected boolean doAddVersion(String edition, UrlVersion urlVersion, String url, OperatingSystem os, SystemArchitecture architecture,
282282
String checksum) {
@@ -297,8 +297,7 @@ protected boolean doAddVersion(String edition, UrlVersion urlVersion, String url
297297
}
298298
url = url.replace("${edition}", edition);
299299

300-
return checkDownloadUrl(edition, url, urlVersion, os, architecture, checksum);
301-
300+
return doAddVersionUrlIfNewAndValid(edition, url, urlVersion, os, architecture, checksum);
302301
}
303302

304303
/**
@@ -388,9 +387,16 @@ protected boolean isValidContentType(String contentType) {
388387
* @param checksum the existing checksum (e.g. from JSON metadata) or the empty {@link String} if not available and computation needed.
389388
* @return {@code true} if the download was checked successfully, {@code false} otherwise.
390389
*/
391-
private boolean checkDownloadUrl(String edition, String url, UrlVersion urlVersion, OperatingSystem os,
390+
private boolean doAddVersionUrlIfNewAndValid(String edition, String url, UrlVersion urlVersion, OperatingSystem os,
392391
SystemArchitecture architecture, String checksum) {
393392

393+
UrlDownloadFile urlDownloadFile = urlVersion.getUrls(os, architecture);
394+
if (urlDownloadFile != null) {
395+
if (urlDownloadFile.getUrls().contains(url)) {
396+
logger.debug("Skipping add of already existing URL {}", url);
397+
return false;
398+
}
399+
}
394400
HttpResponse<?> response = doCheckDownloadViaHeadRequest(url);
395401
int statusCode = response.statusCode();
396402
String toolWithEdition = getToolWithEdition(edition);
@@ -400,7 +406,6 @@ private boolean checkDownloadUrl(String edition, String url, UrlVersion urlVersi
400406

401407
boolean update = false;
402408

403-
UrlDownloadFile urlDownloadFile = urlVersion.getUrls(os, architecture);
404409
if (success) {
405410
UrlChecksum urlChecksum = null;
406411
if (urlDownloadFile != null) {
@@ -661,7 +666,7 @@ protected Set<String> getUrlFilenames() {
661666
/**
662667
* Checks if we are dependent on OS URL file names, can be overridden to disable OS dependency
663668
*
664-
* @return true if we want to check for missing OS URL file names, false if not
669+
* @return {@code true} if we want to check for missing OS URL file names, {@code false} if not.
665670
*/
666671
protected boolean isOsDependent() {
667672

@@ -672,7 +677,7 @@ protected boolean isOsDependent() {
672677
* Checks if an OS URL file name was missing in {@link UrlVersion}
673678
*
674679
* @param urlVersion the {@link UrlVersion} to check
675-
* @return true if an OS type was missing, false if not
680+
* @return {@code true} if an OS type was missing, {@code false} if not.
676681
*/
677682
public boolean isMissingOs(UrlVersion urlVersion) {
678683

cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterMock.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import java.util.HashSet;
55
import java.util.Set;
66

7-
import com.devonfw.tools.ide.url.model.folder.UrlRepository;
87
import com.devonfw.tools.ide.url.model.folder.UrlVersion;
98
import com.devonfw.tools.ide.url.updater.AbstractUrlUpdater;
109
import com.devonfw.tools.ide.url.updater.UrlUpdater;
@@ -35,11 +34,6 @@ protected String getTool() {
3534
return "mocked";
3635
}
3736

38-
@Override
39-
public void update(UrlRepository urlRepository) {
40-
super.update(urlRepository);
41-
}
42-
4337
@Override
4438
protected Set<String> getVersions() {
4539
return versions;

cli/src/test/java/com/devonfw/tools/ide/tool/UrlUpdaterTest.java

Lines changed: 131 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,25 @@
88
import java.io.IOException;
99
import java.nio.file.Files;
1010
import java.nio.file.Path;
11+
import java.time.Duration;
1112
import java.time.Instant;
13+
import java.time.temporal.ChronoUnit;
1214

1315
import org.junit.jupiter.api.Test;
1416
import org.junit.jupiter.api.io.TempDir;
1517

18+
import com.devonfw.tools.ide.os.OperatingSystem;
19+
import com.devonfw.tools.ide.os.SystemArchitecture;
20+
import com.devonfw.tools.ide.url.model.file.UrlChecksum;
21+
import com.devonfw.tools.ide.url.model.file.UrlDownloadFile;
22+
import com.devonfw.tools.ide.url.model.file.UrlStatusFile;
1623
import com.devonfw.tools.ide.url.model.file.json.StatusJson;
1724
import com.devonfw.tools.ide.url.model.file.json.UrlStatus;
25+
import com.devonfw.tools.ide.url.model.file.json.UrlStatusState;
26+
import com.devonfw.tools.ide.url.model.folder.UrlEdition;
1827
import com.devonfw.tools.ide.url.model.folder.UrlRepository;
28+
import com.devonfw.tools.ide.url.model.folder.UrlTool;
29+
import com.devonfw.tools.ide.url.model.folder.UrlVersion;
1930
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
2031
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
2132

@@ -93,80 +104,153 @@ public void testUrlUpdaterIsNotUpdatingWhenStatusManualIsTrue(@TempDir Path temp
93104
}
94105

95106
/**
96-
* Tests if the timestamps of the status.json get updated properly. Creates an initial status.json with a success timestamp. Updates the status.json with an
97-
* error timestamp and compares it with the success timestamp. Updates the status.json with a final success timestamp and compares it with the error
98-
* timestamp.
107+
* Tests if the timestamps of the status.json get updated properly if a first error is detected.
99108
* <p>
100109
* See: <a href="https://github.com/devonfw/ide/issues/1343">#1343</a> for reference.
101110
*
102111
* @param tempDir Temporary directory
103112
* @param wmRuntimeInfo wireMock server on a random port
104113
*/
105114
@Test
106-
public void testUrlUpdaterStatusJsonRefreshBugStillExisting(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
115+
public void testStatusJsonUpdateOnFirstError(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
107116

108117
// arrange
109-
stubFor(any(urlMatching("/os/.*")).willReturn(aResponse().withStatus(200).withBody("aBody")));
110-
111-
UrlRepository urlRepository = UrlRepository.load(tempDir);
112-
UrlUpdaterMockSingle updater = new UrlUpdaterMockSingle(wmRuntimeInfo);
113-
114-
String statusUrl = wmRuntimeInfo.getHttpBaseUrl() + "/os/windows_x64_url.tgz";
115118
String toolName = "mocked";
116119
String editionName = "mocked";
117120
String versionName = "1.0";
121+
String url = wmRuntimeInfo.getHttpBaseUrl() + "/os/windows_x64_url.tgz";
122+
Instant now = Instant.now();
123+
Instant lastMonth = now.minus(31, ChronoUnit.DAYS);
124+
UrlRepository urlRepository = UrlRepository.load(tempDir);
125+
UrlTool urlTool = urlRepository.getOrCreateChild(toolName);
126+
UrlEdition urlEdition = urlTool.getOrCreateChild(editionName);
127+
UrlVersion urlVersion = urlEdition.getOrCreateChild(versionName);
128+
// we create the structure of our tool version and URL to simulate it was valid last moth
129+
UrlStatusFile statusFile = urlVersion.getOrCreateStatus();
130+
UrlStatus status = statusFile.getStatusJson().getOrCreateUrlStatus(url);
131+
UrlStatusState successState = new UrlStatusState(lastMonth); // ensure that we trigger a recheck of the URL
132+
status.setSuccess(successState);
133+
UrlDownloadFile urlDownloadFile = urlVersion.getOrCreateUrls(OperatingSystem.WINDOWS, SystemArchitecture.X64);
134+
urlDownloadFile.addUrl(url);
135+
UrlChecksum urlChecksum = urlVersion.getOrCreateChecksum(urlDownloadFile.getName());
136+
urlChecksum.setChecksum("1234567890");
137+
urlVersion.save();
138+
UrlUpdaterMockSingle updater = new UrlUpdaterMockSingle(wmRuntimeInfo);
139+
// now we want to simulate that the url got broken (404) and the updater is properly handling this
140+
stubFor(any(urlMatching("/os/.*")).willReturn(aResponse().withStatus(404)));
118141

119142
// act
120143
updater.update(urlRepository);
121144

122-
Path versionsPath = tempDir.resolve(toolName).resolve(editionName).resolve(versionName);
123-
124145
// assert
125-
assertThat(versionsPath.resolve("status.json")).exists();
126-
127146
StatusJson statusJson = retrieveStatusJson(urlRepository, toolName, editionName, versionName);
147+
status = statusJson.getStatus(url);
148+
successState = status.getSuccess();
149+
assertThat(successState).isNotNull();
150+
assertThat(successState.getTimestamp()).isEqualTo(lastMonth);
151+
UrlStatusState errorState = status.getError();
152+
assertThat(errorState).isNotNull();
153+
assertThat(errorState.getCode()).isEqualTo(404);
154+
assertThat(Duration.between(errorState.getTimestamp(), now)).isLessThan(Duration.ofSeconds(5));
155+
}
128156

129-
UrlStatus urlStatus = statusJson.getOrCreateUrlStatus(statusUrl);
130-
131-
Instant successTimestamp = urlStatus.getSuccess().getTimestamp();
132-
133-
assertThat(successTimestamp).isNotNull();
134-
135-
stubFor(any(urlMatching("/os/.*")).willReturn(aResponse().withStatus(404)));
136-
137-
// re-initialize UrlRepository for error timestamp
138-
UrlRepository urlRepositoryWithError = UrlRepository.load(tempDir);
139-
updater.update(urlRepositoryWithError);
140-
141-
statusJson = retrieveStatusJson(urlRepositoryWithError, toolName, editionName, versionName);
142-
143-
urlStatus = statusJson.getOrCreateUrlStatus(statusUrl);
144-
successTimestamp = urlStatus.getSuccess().getTimestamp();
145-
Instant errorTimestamp = urlStatus.getError().getTimestamp();
146-
Integer errorCode = urlStatus.getError().getCode();
147-
148-
assertThat(errorCode).isEqualTo(404);
149-
assertThat(errorTimestamp).isAfter(successTimestamp);
157+
/**
158+
* Tests if the timestamps of the status.json get updated properly on success after an error.
159+
* <p>
160+
* See: <a href="https://github.com/devonfw/ide/issues/1343">#1343</a> for reference.
161+
*
162+
* @param tempDir Temporary directory
163+
* @param wmRuntimeInfo wireMock server on a random port
164+
*/
165+
@Test
166+
public void testSuccessStateUpdatedAfterError(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
150167

168+
// arrange
169+
String toolName = "mocked";
170+
String editionName = "mocked";
171+
String versionName = "1.0";
172+
String url = wmRuntimeInfo.getHttpBaseUrl() + "/os/windows_x64_url.tgz";
173+
Instant now = Instant.now();
174+
Instant lastMonth = now.minus(31, ChronoUnit.DAYS);
175+
Instant lastSuccess = lastMonth.minus(1, ChronoUnit.DAYS);
176+
UrlRepository urlRepository = UrlRepository.load(tempDir);
177+
UrlTool urlTool = urlRepository.getOrCreateChild(toolName);
178+
UrlEdition urlEdition = urlTool.getOrCreateChild(editionName);
179+
UrlVersion urlVersion = urlEdition.getOrCreateChild(versionName);
180+
// we create the structure of our tool version and URL to simulate it was valid last moth
181+
UrlStatusFile statusFile = urlVersion.getOrCreateStatus();
182+
UrlStatus status = statusFile.getStatusJson().getOrCreateUrlStatus(url);
183+
UrlStatusState successState = new UrlStatusState(lastSuccess);
184+
status.setSuccess(successState);
185+
UrlStatusState errorState = new UrlStatusState(lastMonth);
186+
errorState.setCode(404);
187+
status.setError(errorState);
188+
UrlDownloadFile urlDownloadFile = urlVersion.getOrCreateUrls(OperatingSystem.WINDOWS, SystemArchitecture.X64);
189+
urlDownloadFile.addUrl(url);
190+
UrlChecksum urlChecksum = urlVersion.getOrCreateChecksum(urlDownloadFile.getName());
191+
urlChecksum.setChecksum("1234567890");
192+
urlVersion.save();
193+
UrlUpdaterMockSingle updater = new UrlUpdaterMockSingle(wmRuntimeInfo);
194+
// now we want to simulate that the broken url is working again
151195
stubFor(any(urlMatching("/os/.*")).willReturn(aResponse().withStatus(200).withHeader("Content-Type", "text/plain")));
152196

153-
// re-initialize UrlRepository for error timestamp
154-
UrlRepository urlRepositoryWithSuccess = UrlRepository.load(tempDir);
155-
updater.update(urlRepositoryWithSuccess);
156-
157-
assertThat(versionsPath.resolve("status.json")).exists();
197+
// act
198+
updater.update(urlRepository);
158199

159-
statusJson = retrieveStatusJson(urlRepositoryWithSuccess, toolName, editionName, versionName);
200+
// assert
201+
status = retrieveStatusJson(urlRepository, toolName, editionName, versionName).getStatus(url);
202+
successState = status.getSuccess();
203+
assertThat(successState).isNotNull();
204+
assertThat(Duration.between(successState.getTimestamp(), now)).isLessThan(Duration.ofSeconds(5));
205+
errorState = status.getError();
206+
assertThat(errorState).isNotNull();
207+
assertThat(errorState.getCode()).isEqualTo(404);
208+
assertThat(errorState.getTimestamp()).isEqualTo(lastMonth);
209+
}
160210

161-
urlStatus = statusJson.getOrCreateUrlStatus(statusUrl);
211+
/**
212+
* Tests if the the tool version gets entirely removed if all versions are broken for a long time.
213+
*
214+
* @param tempDir Temporary directory
215+
* @param wmRuntimeInfo wireMock server on a random port
216+
*/
217+
@Test
218+
public void testVersionRemovedIfErrorPersists(@TempDir Path tempDir, WireMockRuntimeInfo wmRuntimeInfo) {
162219

163-
successTimestamp = urlStatus.getSuccess().getTimestamp();
164-
errorTimestamp = urlStatus.getError().getTimestamp();
165-
errorCode = urlStatus.getError().getCode();
220+
// arrange
221+
String toolName = "mocked";
222+
String editionName = "mocked";
223+
String versionName = "1.0";
224+
String url = wmRuntimeInfo.getHttpBaseUrl() + "/os/windows_x64_url.tgz";
225+
Instant now = Instant.now();
226+
Instant lastMonth = now.minus(31, ChronoUnit.DAYS);
227+
Instant lastSuccess = lastMonth.minus(1, ChronoUnit.DAYS);
228+
UrlRepository urlRepository = UrlRepository.load(tempDir);
229+
UrlTool urlTool = urlRepository.getOrCreateChild(toolName);
230+
UrlEdition urlEdition = urlTool.getOrCreateChild(editionName);
231+
UrlVersion urlVersion = urlEdition.getOrCreateChild(versionName);
232+
// we create the structure of our tool version and URL to simulate it was valid last moth
233+
UrlStatusFile statusFile = urlVersion.getOrCreateStatus();
234+
UrlStatus status = statusFile.getStatusJson().getOrCreateUrlStatus(url);
235+
UrlStatusState successState = new UrlStatusState(lastSuccess);
236+
status.setSuccess(successState);
237+
UrlStatusState errorState = new UrlStatusState(lastMonth);
238+
errorState.setCode(404);
239+
status.setError(errorState);
240+
UrlDownloadFile urlDownloadFile = urlVersion.getOrCreateUrls(OperatingSystem.WINDOWS, SystemArchitecture.X64);
241+
urlDownloadFile.addUrl(url);
242+
UrlChecksum urlChecksum = urlVersion.getOrCreateChecksum(urlDownloadFile.getName());
243+
urlChecksum.setChecksum("1234567890");
244+
urlVersion.save();
245+
UrlUpdaterMockSingle updater = new UrlUpdaterMockSingle(wmRuntimeInfo);
246+
// now we want to simulate that the url got broken (404) and the updater is properly handling this
247+
stubFor(any(urlMatching("/os/.*")).willReturn(aResponse().withStatus(404)));
166248

167-
assertThat(errorCode).isEqualTo(200);
168-
assertThat(errorTimestamp).isAfter(successTimestamp);
249+
// act
250+
updater.update(urlRepository);
169251

252+
// assert
253+
assertThat(urlVersion.getPath()).doesNotExist();
170254
}
171255

172256
/**

0 commit comments

Comments
 (0)