From 083e5a711e631995b7b6cfac57cfadc8e13871cb Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Sat, 25 Nov 2023 22:48:30 +0100 Subject: [PATCH 1/6] chore: move DownloadException and DownloadUtils to `remote` package --- src/main/java/org/terasology/launcher/game/GameManager.java | 4 ++-- .../launcher/{util => remote}/DownloadException.java | 4 ++-- .../terasology/launcher/{util => remote}/DownloadUtils.java | 4 ++-- src/main/java/org/terasology/launcher/tasks/DownloadTask.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) rename src/main/java/org/terasology/launcher/{util => remote}/DownloadException.java (85%) rename src/main/java/org/terasology/launcher/{util => remote}/DownloadUtils.java (98%) diff --git a/src/main/java/org/terasology/launcher/game/GameManager.java b/src/main/java/org/terasology/launcher/game/GameManager.java index ffbf7911..85a715e2 100644 --- a/src/main/java/org/terasology/launcher/game/GameManager.java +++ b/src/main/java/org/terasology/launcher/game/GameManager.java @@ -13,8 +13,8 @@ import org.terasology.launcher.model.GameRelease; import org.terasology.launcher.model.Profile; import org.terasology.launcher.tasks.ProgressListener; -import org.terasology.launcher.util.DownloadException; -import org.terasology.launcher.util.DownloadUtils; +import org.terasology.launcher.remote.DownloadException; +import org.terasology.launcher.remote.DownloadUtils; import org.terasology.launcher.util.FileUtils; import java.io.File; diff --git a/src/main/java/org/terasology/launcher/util/DownloadException.java b/src/main/java/org/terasology/launcher/remote/DownloadException.java similarity index 85% rename from src/main/java/org/terasology/launcher/util/DownloadException.java rename to src/main/java/org/terasology/launcher/remote/DownloadException.java index 43961be1..77eb6803 100644 --- a/src/main/java/org/terasology/launcher/util/DownloadException.java +++ b/src/main/java/org/terasology/launcher/remote/DownloadException.java @@ -1,7 +1,7 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2023 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.launcher.util; +package org.terasology.launcher.remote; public final class DownloadException extends RuntimeException { diff --git a/src/main/java/org/terasology/launcher/util/DownloadUtils.java b/src/main/java/org/terasology/launcher/remote/DownloadUtils.java similarity index 98% rename from src/main/java/org/terasology/launcher/util/DownloadUtils.java rename to src/main/java/org/terasology/launcher/remote/DownloadUtils.java index 0d711ebc..bfec4f94 100644 --- a/src/main/java/org/terasology/launcher/util/DownloadUtils.java +++ b/src/main/java/org/terasology/launcher/remote/DownloadUtils.java @@ -1,7 +1,7 @@ -// Copyright 2021 The Terasology Foundation +// Copyright 2023 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.launcher.util; +package org.terasology.launcher.remote; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/terasology/launcher/tasks/DownloadTask.java b/src/main/java/org/terasology/launcher/tasks/DownloadTask.java index 8983e161..b01dc475 100644 --- a/src/main/java/org/terasology/launcher/tasks/DownloadTask.java +++ b/src/main/java/org/terasology/launcher/tasks/DownloadTask.java @@ -8,7 +8,7 @@ import org.slf4j.LoggerFactory; import org.terasology.launcher.game.GameManager; import org.terasology.launcher.model.GameRelease; -import org.terasology.launcher.util.DownloadException; +import org.terasology.launcher.remote.DownloadException; import java.io.IOException; From 8f69b733beceb9dfc0b65a9a9cd21e46905034dc Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Sat, 25 Nov 2023 22:48:45 +0100 Subject: [PATCH 2/6] feat: add `RemoteResource` interface --- .../launcher/remote/RemoteResource.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/org/terasology/launcher/remote/RemoteResource.java diff --git a/src/main/java/org/terasology/launcher/remote/RemoteResource.java b/src/main/java/org/terasology/launcher/remote/RemoteResource.java new file mode 100644 index 00000000..74f6d9c5 --- /dev/null +++ b/src/main/java/org/terasology/launcher/remote/RemoteResource.java @@ -0,0 +1,17 @@ +// Copyright 2023 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.launcher.remote; + +import java.net.URL; + +public interface RemoteResource { + + URL getUrl(); + + String getFilename(); + + T getInfo(); + + //TODO: String getChecksum(); +} From 09218f3971b26cd1d057b3cbb90593784d1390be Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Sat, 25 Nov 2023 22:52:05 +0100 Subject: [PATCH 3/6] feat: make GameRelease a RemoteResource --- .../terasology/launcher/game/GameManager.java | 7 ++----- .../terasology/launcher/model/GameRelease.java | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/terasology/launcher/game/GameManager.java b/src/main/java/org/terasology/launcher/game/GameManager.java index 85a715e2..b54f40bb 100644 --- a/src/main/java/org/terasology/launcher/game/GameManager.java +++ b/src/main/java/org/terasology/launcher/game/GameManager.java @@ -57,12 +57,9 @@ public GameManager(Path cacheDirectory, Path installDirectory) { /** * Derive the file name for the downloaded ZIP package from the game release. */ + @Deprecated private String getFileNameFor(GameRelease release) { - GameIdentifier id = release.getId(); - String profileString = id.getProfile().toString().toLowerCase(); - String versionString = id.getDisplayVersion(); - String buildString = id.getBuild().toString().toLowerCase(); - return "terasology-" + profileString + "-" + versionString + "-" + buildString + ".zip"; + return release.getFilename(); } /** diff --git a/src/main/java/org/terasology/launcher/model/GameRelease.java b/src/main/java/org/terasology/launcher/model/GameRelease.java index 9a66e7d4..deb4e02c 100644 --- a/src/main/java/org/terasology/launcher/model/GameRelease.java +++ b/src/main/java/org/terasology/launcher/model/GameRelease.java @@ -3,6 +3,8 @@ package org.terasology.launcher.model; +import org.terasology.launcher.remote.RemoteResource; + import java.net.URL; import java.util.Date; import java.util.Objects; @@ -17,7 +19,7 @@ *
  • TODO: define what the artifact is, and what requirements/restrictions there are
  • * */ -public class GameRelease { +public class GameRelease implements RemoteResource { final GameIdentifier id; final ReleaseMetadata releaseMetadata; final URL url; @@ -36,6 +38,19 @@ public URL getUrl() { return url; } + @Override + public String getFilename() { + String profileString = id.getProfile().toString().toLowerCase(); + String versionString = id.getDisplayVersion(); + String buildString = id.getBuild().toString().toLowerCase(); + return "terasology-" + profileString + "-" + versionString + "-" + buildString + ".zip"; + } + + @Override + public GameIdentifier getInfo() { + return id; + } + /** * The changelog associated with the game release */ From e1752668c886b18f3eef0a415b9c511ff9f85beb Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Sun, 26 Nov 2023 12:11:38 +0100 Subject: [PATCH 4/6] chore: implement GameManager#download with DownloadUtils --- .../terasology/launcher/game/GameManager.java | 35 ++++------ .../launcher/remote/DownloadUtils.java | 65 +++++++++++++++---- 2 files changed, 64 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/terasology/launcher/game/GameManager.java b/src/main/java/org/terasology/launcher/game/GameManager.java index b54f40bb..0cd27d8d 100644 --- a/src/main/java/org/terasology/launcher/game/GameManager.java +++ b/src/main/java/org/terasology/launcher/game/GameManager.java @@ -12,18 +12,17 @@ import org.terasology.launcher.model.GameIdentifier; import org.terasology.launcher.model.GameRelease; import org.terasology.launcher.model.Profile; -import org.terasology.launcher.tasks.ProgressListener; import org.terasology.launcher.remote.DownloadException; import org.terasology.launcher.remote.DownloadUtils; +import org.terasology.launcher.remote.RemoteResource; +import org.terasology.launcher.tasks.ProgressListener; import org.terasology.launcher.util.FileUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.Comparator; import java.util.Objects; import java.util.Set; @@ -84,30 +83,18 @@ public void install(GameRelease release, ProgressListener listener) throws IOExc } } + /** + * @deprecated Use {@link DownloadUtils#download(RemoteResource, Path, ProgressListener)} instead. + */ + @Deprecated private void download(GameRelease release, Path targetLocation, ProgressListener listener) throws DownloadException, IOException, InterruptedException { - final URL downloadUrl = release.getUrl(); - - final long contentLength = DownloadUtils.getContentLength(downloadUrl); - final long availableSpace = targetLocation.getParent().toFile().getUsableSpace(); - - if (availableSpace >= contentLength) { - final Path cacheZipPart = targetLocation.resolveSibling(targetLocation.getFileName().toString() + ".part"); - Files.deleteIfExists(cacheZipPart); - try { - DownloadUtils.downloadToFile(downloadUrl, cacheZipPart, listener).get(); - } catch (ExecutionException e) { - throw new DownloadException("Exception while downloading " + downloadUrl, e.getCause()); - } - - if (!listener.isCancelled()) { - Files.move(cacheZipPart, targetLocation, StandardCopyOption.ATOMIC_MOVE); - } - } else { - throw new DownloadException("Insufficient space for downloading package"); + DownloadUtils downloader = new DownloadUtils(); + try { + downloader.download(release, targetLocation, listener).get(); + } catch (ExecutionException e) { + throw new DownloadException("Download failed.", e.getCause()); } - - logger.info("Finished downloading package: {}", release.getId()); } /** diff --git a/src/main/java/org/terasology/launcher/remote/DownloadUtils.java b/src/main/java/org/terasology/launcher/remote/DownloadUtils.java index bfec4f94..9beb28a0 100644 --- a/src/main/java/org/terasology/launcher/remote/DownloadUtils.java +++ b/src/main/java/org/terasology/launcher/remote/DownloadUtils.java @@ -7,11 +7,11 @@ import org.slf4j.LoggerFactory; import org.terasology.launcher.tasks.ProgressListener; +import javax.net.ssl.HttpsURLConnection; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; import java.net.URISyntaxException; import java.net.URL; import java.net.http.HttpClient; @@ -19,19 +19,63 @@ import java.net.http.HttpResponse; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.time.Duration; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; public final class DownloadUtils { private static final Logger logger = LoggerFactory.getLogger(DownloadUtils.class); - private static final Duration CONNECT_TIMEOUT = Duration.ofSeconds(30); - private static final Duration READ_TIMEOUT = Duration.ofMinutes(5); + private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(30); + private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofMinutes(5); - private DownloadUtils() { + private final Duration connectTimeout; //TODO: use instead of default + private final Duration readTimeout; //TODO: use instead of default + + public DownloadUtils() { + this(DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT); + } + + public DownloadUtils(Duration connectTimeout, Duration readTimeout) { + this.connectTimeout = connectTimeout; + this.readTimeout = readTimeout; + } + + public CompletableFuture download(RemoteResource resource, Path path, ProgressListener listener) + throws DownloadException, IOException, InterruptedException { + final URL downloadUrl = resource.getUrl(); + + final long contentLength = DownloadUtils.getContentLength(downloadUrl); + final long availableSpace = path.getParent().toFile().getUsableSpace(); + + if (availableSpace >= contentLength) { + final Path cacheZipPart = path.resolveSibling(path.getFileName().toString() + ".part"); + Files.deleteIfExists(cacheZipPart); + try { + DownloadUtils.downloadToFile(downloadUrl, cacheZipPart, listener).get(); + } catch (ExecutionException e) { + throw new DownloadException("Exception while downloading " + downloadUrl, e.getCause()); + } + + if (!listener.isCancelled()) { + Files.move(cacheZipPart, path, StandardCopyOption.ATOMIC_MOVE); + } + } else { + throw new DownloadException("Insufficient space for downloading package"); + } + + logger.info("Finished downloading package: {}", resource.getInfo()); + + + return CompletableFuture.supplyAsync(() -> path); } + /** + * @deprecated Use {@link #download(RemoteResource, Path, ProgressListener)} instead; + */ + @Deprecated public static CompletableFuture downloadToFile(URL downloadURL, Path file, ProgressListener listener) throws DownloadException { listener.update(0); @@ -62,30 +106,27 @@ public static CompletableFuture downloadToFile(URL downloadURL, Path file, }); } + @Deprecated public static long getContentLength(URL downloadURL) throws DownloadException { - HttpURLConnection connection = null; + HttpsURLConnection connection = null; try { - connection = (HttpURLConnection) downloadURL.openConnection(); + connection = (HttpsURLConnection) downloadURL.openConnection(); connection.setRequestMethod("HEAD"); return connection.getContentLengthLong(); } catch (IOException e) { throw new DownloadException("Could not send HEAD request to HTTP-URL! URL=" + downloadURL, e); - } finally { - if (connection != null) { - connection.disconnect(); - } } } private static CompletableFuture> getConnectedDownloadConnection(URL downloadURL) throws DownloadException { var client = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.NORMAL) - .connectTimeout(CONNECT_TIMEOUT) + .connectTimeout(DEFAULT_CONNECT_TIMEOUT) .build(); HttpRequest request; try { - request = HttpRequest.newBuilder(downloadURL.toURI()).timeout(READ_TIMEOUT).build(); + request = HttpRequest.newBuilder(downloadURL.toURI()).timeout(DEFAULT_READ_TIMEOUT).build(); } catch (URISyntaxException e) { throw new DownloadException("Error in URL: " + downloadURL, e); } From 482ba65fe0bfe8597d2b1da2f6e1ddc92e44184d Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Sat, 22 Jun 2024 11:41:08 +0200 Subject: [PATCH 5/6] inline use of (deprecated) GameManager::getFileNameFor --- .../java/org/terasology/launcher/game/GameManager.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/main/java/org/terasology/launcher/game/GameManager.java b/src/main/java/org/terasology/launcher/game/GameManager.java index f4c07869..3429f0bc 100644 --- a/src/main/java/org/terasology/launcher/game/GameManager.java +++ b/src/main/java/org/terasology/launcher/game/GameManager.java @@ -52,14 +52,6 @@ public GameManager(Path cacheDirectory, Path installDirectory) { scanInstallationDir(); } - /** - * Derive the file name for the downloaded ZIP package from the game release. - */ - @Deprecated - private String getFileNameFor(GameRelease release) { - return release.getFilename(); - } - /** * Installs the given release to the local file system. * @@ -67,7 +59,7 @@ private String getFileNameFor(GameRelease release) { * @param listener the object which is to be informed about task progress */ public void install(GameRelease release, ProgressListener listener) throws IOException, DownloadException, InterruptedException { - final Path cachedZip = cacheDirectory.resolve(getFileNameFor(release)); + final Path cachedZip = cacheDirectory.resolve(release.getFilename()); // TODO: Properly validate cache and handle exceptions if (Files.notExists(cachedZip)) { From a2482857d425e8e113b8b454b50de6cd60f05a8b Mon Sep 17 00:00:00 2001 From: jdrueckert Date: Sat, 22 Jun 2024 12:25:02 +0200 Subject: [PATCH 6/6] chore: remove unused import --- src/main/java/org/terasology/launcher/game/GameManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/terasology/launcher/game/GameManager.java b/src/main/java/org/terasology/launcher/game/GameManager.java index 3429f0bc..d6b236d0 100644 --- a/src/main/java/org/terasology/launcher/game/GameManager.java +++ b/src/main/java/org/terasology/launcher/game/GameManager.java @@ -10,7 +10,6 @@ import org.slf4j.LoggerFactory; import org.terasology.launcher.model.GameIdentifier; import org.terasology.launcher.model.GameRelease; -import org.terasology.launcher.model.Profile; import org.terasology.launcher.remote.DownloadException; import org.terasology.launcher.remote.DownloadUtils; import org.terasology.launcher.remote.RemoteResource;