From 34a3ee1d38bd3c2092ecce5c52d78e129c7f96fc Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Tue, 25 Jun 2024 14:26:36 +0200 Subject: [PATCH] fnihs work im so tired rn --- pom.xml | 15 ++++ src/main/java/eu/m724/jarupdater/Updater.java | 22 ++++- .../m724/jarupdater/download/Downloader.java | 13 +++ .../jarupdater/download/SimpleDownloader.java | 2 +- .../jarupdater/live/GiteaMetadataDAO.java | 81 +++++++++++++++++++ .../eu/m724/jarupdater/live/MetadataDAO.java | 17 +++- .../m724/jarupdater/live/MetadataFacade.java | 6 +- .../object/NoSuchVersionException.java | 13 +++ .../eu/m724/jarupdater/object/Version.java | 10 ++- src/test/java/jarupdater/DownloaderTest.java | 46 +++++++++++ src/test/java/jarupdater/EnvironmentTest.java | 19 +++++ src/test/java/jarupdater/MetadataTest.java | 44 ++++++++++ src/test/java/jarupdater/MockDownloader.java | 44 ++++++++++ src/test/java/jarupdater/MockMetadataDAO.java | 43 ++++++++++ 14 files changed, 365 insertions(+), 10 deletions(-) create mode 100644 src/main/java/eu/m724/jarupdater/live/GiteaMetadataDAO.java create mode 100644 src/main/java/eu/m724/jarupdater/object/NoSuchVersionException.java create mode 100644 src/test/java/jarupdater/DownloaderTest.java create mode 100644 src/test/java/jarupdater/EnvironmentTest.java create mode 100644 src/test/java/jarupdater/MetadataTest.java create mode 100644 src/test/java/jarupdater/MockDownloader.java create mode 100644 src/test/java/jarupdater/MockMetadataDAO.java diff --git a/pom.xml b/pom.xml index cba4e3d..5138603 100644 --- a/pom.xml +++ b/pom.xml @@ -16,6 +16,21 @@ https://git.724.rocks/api/packages/Minecon724/maven + + + + com.google.code.gson + gson + 2.11.0 + true + + + junit + junit + 4.13.2 + test + + diff --git a/src/main/java/eu/m724/jarupdater/Updater.java b/src/main/java/eu/m724/jarupdater/Updater.java index 6dabea7..be07850 100644 --- a/src/main/java/eu/m724/jarupdater/Updater.java +++ b/src/main/java/eu/m724/jarupdater/Updater.java @@ -1,8 +1,11 @@ package eu.m724.jarupdater; import java.io.File; +import java.io.IOException; import java.nio.file.NoSuchFileException; +import java.security.SignatureException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import eu.m724.jarupdater.download.Downloader; import eu.m724.jarupdater.environment.Environment; @@ -26,6 +29,10 @@ public class Updater { return environment; } + /** + * get the latest available version + * @return a future which can throw ioexpception + */ public CompletableFuture getLatestVersion() { CompletableFuture currentVersionFuture = metadataProvider.getCurrentVersionMetadata(); CompletableFuture latestVersionFuture = metadataProvider.getLatestVersionMetadata(); @@ -34,14 +41,25 @@ public class Updater { Version currentVersion = currentVersionFuture.join(); Version latestVersion = latestVersionFuture.join(); + if (currentVersion == null || latestVersion == null) + throw new CompletionException(new IOException()); + return latestVersion.getId() > currentVersion.getId() ? latestVersion : null; }); } - + + /** + * get information about this version + * @return a future which can throw ioexpception + */ public CompletableFuture getCurrentVersion() { return metadataProvider.getCurrentVersionMetadata(); } - + + /** + * download the latest available version + * @return a future which returns the downloaded file, it can also throw ioexpception or signatureexception + */ public CompletableFuture downloadLatestVersion() { CompletableFuture latestVersionFuture = metadataProvider.getLatestVersionMetadata(); diff --git a/src/main/java/eu/m724/jarupdater/download/Downloader.java b/src/main/java/eu/m724/jarupdater/download/Downloader.java index 4863214..0cccc0a 100644 --- a/src/main/java/eu/m724/jarupdater/download/Downloader.java +++ b/src/main/java/eu/m724/jarupdater/download/Downloader.java @@ -4,6 +4,19 @@ import java.io.File; import java.util.concurrent.CompletableFuture; public interface Downloader { + /** + * downloads a file and verifies it + * @param url + * @param sha256hex + * @return a future which can throw ioexception or signatureexception + */ public CompletableFuture downloadAndVerify(String url, String sha256hex); + + /** + * moves source into destination + * @param source + * @param destination + * @return a future which can throw ioexception + */ public CompletableFuture install(File source, File destination); } diff --git a/src/main/java/eu/m724/jarupdater/download/SimpleDownloader.java b/src/main/java/eu/m724/jarupdater/download/SimpleDownloader.java index 416b0d7..ca74fd3 100644 --- a/src/main/java/eu/m724/jarupdater/download/SimpleDownloader.java +++ b/src/main/java/eu/m724/jarupdater/download/SimpleDownloader.java @@ -64,7 +64,7 @@ public class SimpleDownloader implements Downloader { return downloadedFile; - } catch (IOException | NoSuchAlgorithmException | SignatureException e) { + } catch (IOException | NoSuchAlgorithmException | SignatureException e) { // TODO nosuchalgo should not be thrown throw new CompletionException(e); } diff --git a/src/main/java/eu/m724/jarupdater/live/GiteaMetadataDAO.java b/src/main/java/eu/m724/jarupdater/live/GiteaMetadataDAO.java new file mode 100644 index 0000000..8483fc6 --- /dev/null +++ b/src/main/java/eu/m724/jarupdater/live/GiteaMetadataDAO.java @@ -0,0 +1,81 @@ +package eu.m724.jarupdater.live; + +import java.io.IOException; +import java.net.ProxySelector; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.net.http.HttpClient.Redirect; +import java.net.http.HttpResponse.BodyHandlers; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import com.google.gson.Gson; + +import eu.m724.jarupdater.object.Version; + +public class GiteaMetadataDAO implements MetadataDAO { + private String url; + private String branch; + + public GiteaMetadataDAO(String url, String branch) { + this.url = url; + this.branch = branch; + } + + + @Override + public CompletableFuture> getChannels() { + String url = getFileUrl("channels.txt"); + + CompletableFuture> channelsFuture = + makeRequest(url).thenApply(response -> { + if (response.statusCode() != 200) + throw new CompletionException(new IOException("Server returned status code %d".formatted(response.statusCode()))); + + return response.body().lines().toList(); + }); + + return channelsFuture; + } + + @Override + public CompletableFuture getMetadata(String channel, String versionLabel) { + String url = getFileUrl(versionLabel, "meta-v1.json"); + + CompletableFuture metadataFuture = + makeRequest(url).thenApply(response -> { + if (response.statusCode() != 200) + throw new CompletionException(new IOException("Server returned status code %d".formatted(response.statusCode()))); + // not throwing nosuchversionexecpion because it's not possible to tell if the server is broken or it really doesn't exist + + Version version = new Gson().fromJson(response.body(), Version.class); + return version; + }); + + return metadataFuture; + } + + + private CompletableFuture> makeRequest(String url) { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("User-Agent", "ju/1") // jar updater v1 + .build(); + + CompletableFuture> responseFuture = + HttpClient.newBuilder() + .followRedirects(Redirect.NORMAL) + .proxy(ProxySelector.getDefault()).build(). + sendAsync(request, BodyHandlers.ofString()); + + return responseFuture; + } + + private String getFileUrl(String... paths) { + return url + "/raw/branch/" + branch + "/data/" + String.join("/", paths); + } + +} diff --git a/src/main/java/eu/m724/jarupdater/live/MetadataDAO.java b/src/main/java/eu/m724/jarupdater/live/MetadataDAO.java index aef387c..99e26cb 100644 --- a/src/main/java/eu/m724/jarupdater/live/MetadataDAO.java +++ b/src/main/java/eu/m724/jarupdater/live/MetadataDAO.java @@ -1,11 +1,22 @@ package eu.m724.jarupdater.live; -import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; import eu.m724.jarupdater.object.Version; public interface MetadataDAO { - public CompletableFuture> getChannels(); - public CompletableFuture getMetadata(String channel, String version); + /** + * get available channels online + * @return a future which can throw ioexception + */ + public CompletableFuture> getChannels(); + + /** + * get metadata of a version in channel + * @param channel + * @param versionLabel + * @return a future which can throw ioexcpeitons or nosuchfilexxception + */ + public CompletableFuture getMetadata(String channel, String versionLabel); } diff --git a/src/main/java/eu/m724/jarupdater/live/MetadataFacade.java b/src/main/java/eu/m724/jarupdater/live/MetadataFacade.java index 5402849..f212601 100644 --- a/src/main/java/eu/m724/jarupdater/live/MetadataFacade.java +++ b/src/main/java/eu/m724/jarupdater/live/MetadataFacade.java @@ -1,7 +1,7 @@ package eu.m724.jarupdater.live; -import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.concurrent.CompletableFuture; import eu.m724.jarupdater.environment.Environment; @@ -12,14 +12,14 @@ public class MetadataFacade { private MetadataDAO metadataDao; private HashMap> cache = new HashMap<>(); - private CompletableFuture> channels = null; + private CompletableFuture> channels = null; public MetadataFacade(Environment environment, MetadataDAO metadataDao) { this.environment = environment; this.metadataDao = metadataDao; } - public CompletableFuture> getChannels() { + public CompletableFuture> getChannels() { if (channels == null) channels = metadataDao.getChannels(); diff --git a/src/main/java/eu/m724/jarupdater/object/NoSuchVersionException.java b/src/main/java/eu/m724/jarupdater/object/NoSuchVersionException.java new file mode 100644 index 0000000..4ead06e --- /dev/null +++ b/src/main/java/eu/m724/jarupdater/object/NoSuchVersionException.java @@ -0,0 +1,13 @@ +package eu.m724.jarupdater.object; + +import java.io.IOException; + +public class NoSuchVersionException extends IOException { + + private static final long serialVersionUID = 8435964987348892266L; + + public NoSuchVersionException(String version) { + super(version); + } + +} diff --git a/src/main/java/eu/m724/jarupdater/object/Version.java b/src/main/java/eu/m724/jarupdater/object/Version.java index 37dfdc0..0bdd276 100644 --- a/src/main/java/eu/m724/jarupdater/object/Version.java +++ b/src/main/java/eu/m724/jarupdater/object/Version.java @@ -5,7 +5,15 @@ public class Version { * metadata file version */ public static final int SPEC = 1; - + + public Version(int id, long timestamp, String label, String fileUrl, String sha256) { + this.id = id; + this.timestamp = timestamp; + this.label = label; + this.fileUrl = fileUrl; + this.sha256 = sha256; + } + private int id; private long timestamp; private String label; diff --git a/src/test/java/jarupdater/DownloaderTest.java b/src/test/java/jarupdater/DownloaderTest.java new file mode 100644 index 0000000..5b2020c --- /dev/null +++ b/src/test/java/jarupdater/DownloaderTest.java @@ -0,0 +1,46 @@ +package jarupdater; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.IOException; +import java.security.SignatureException; +import java.util.concurrent.CompletionException; + +import org.junit.Test; + +import eu.m724.jarupdater.download.Downloader; + +public class DownloaderTest { + @Test + public void testDownloader() { + Downloader downloader = new MockDownloader(); + + File file = downloader.downloadAndVerify("good", "a9194ba3e955ba7482c6552894d4ca41b9bbafd86f4d90f3564c02fcb9e917c2").join(); + assertNotNull(file); + + try { + downloader.downloadAndVerify("invalid", "678af2539cb4156f4e092d5e80de23aed7d9355774697267979b94e5cceec1b2").join(); + fail("this should have thrown"); + } catch (CompletionException e) { + assert e.getCause() instanceof IOException; + } + + try { + downloader.downloadAndVerify("bad", "1dd7d814fa99d923eee9e483c25a02346c47f84dbc160a1a9f87e9b051e77ee1").join(); + fail("this should have thrown"); + } catch (CompletionException e) { + assert e.getCause() instanceof SignatureException; + } + + downloader.install(file, file).join(); + try { + downloader.install(new File("ialsoexistfortesting"), file).join(); + fail("this should have thrown"); + } catch (CompletionException e) { + assert e.getCause() instanceof IOException; + } + + } +} diff --git a/src/test/java/jarupdater/EnvironmentTest.java b/src/test/java/jarupdater/EnvironmentTest.java new file mode 100644 index 0000000..9e0c9f9 --- /dev/null +++ b/src/test/java/jarupdater/EnvironmentTest.java @@ -0,0 +1,19 @@ +package jarupdater; + +import java.nio.file.Path; + +import org.junit.Test; + +import eu.m724.jarupdater.environment.ConstantEnvironment; +import eu.m724.jarupdater.environment.Environment; + +public class EnvironmentTest { + @Test + public void testConstantEnvironment() { + Environment environment = new ConstantEnvironment("1.0", "stable", Path.of("idontexist")); + + assert environment.getRunningVersion().equals("1.0"); + assert environment.getChannel().equals("stable"); + assert environment.getRunningJarFilePath().equals(Path.of("idontexist")); + } +} diff --git a/src/test/java/jarupdater/MetadataTest.java b/src/test/java/jarupdater/MetadataTest.java new file mode 100644 index 0000000..1f2cb75 --- /dev/null +++ b/src/test/java/jarupdater/MetadataTest.java @@ -0,0 +1,44 @@ +package jarupdater; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.concurrent.CompletionException; + +import org.junit.Test; + +import eu.m724.jarupdater.environment.ConstantEnvironment; +import eu.m724.jarupdater.environment.Environment; +import eu.m724.jarupdater.live.MetadataDAO; +import eu.m724.jarupdater.live.MetadataFacade; +import eu.m724.jarupdater.object.Version; + +public class MetadataTest { + @Test + public void testMetadata() { + Environment environment = new ConstantEnvironment("1.0", "stable", Path.of("idontexist")); + + MetadataDAO dao = new MockMetadataDAO(); + MetadataFacade facade = new MetadataFacade(environment, dao); + + assert facade.getChannels().join().contains(environment.getChannel()); + + Version cur = facade.getCurrentVersionMetadata().join(); + Version lat = facade.getLatestVersionMetadata().join(); + + assert cur != null; + assert lat != null; + + assertFalse(cur.equals(lat)); + assertTrue(cur.getLabel().equals(environment.getRunningVersion())); + assertTrue(lat.getLabel().equals("1.1")); + + try { + facade.getVersionMetadata("invalidversion").join(); + fail("this should have thrown"); + } catch (CompletionException e) { + assert e.getCause() instanceof IOException; + } + } +} diff --git a/src/test/java/jarupdater/MockDownloader.java b/src/test/java/jarupdater/MockDownloader.java new file mode 100644 index 0000000..df28d94 --- /dev/null +++ b/src/test/java/jarupdater/MockDownloader.java @@ -0,0 +1,44 @@ +package jarupdater; + +import java.io.File; +import java.io.IOException; +import java.security.SignatureException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import eu.m724.jarupdater.download.Downloader; + +public class MockDownloader implements Downloader { + + @Override + public CompletableFuture downloadAndVerify(String url, String sha256hex) { + CompletableFuture future = null; + + switch (url) { + case "good": + future = CompletableFuture.completedFuture(new File("ionlyexistfortesting")); + break; + case "invalid": + future = CompletableFuture.failedFuture(new CompletionException(new IOException())); + break; + case "bad": + future = CompletableFuture.failedFuture(new CompletionException(new SignatureException())); + break; + } + + return future; + } + + @Override + public CompletableFuture install(File source, File destination) { + CompletableFuture future = null; + + if (source.getName().equals("ionlyexistfortesting")) + future = CompletableFuture.completedFuture(null); + else + future = CompletableFuture.failedFuture(new CompletionException(new IOException())); + + return future; + } + +} diff --git a/src/test/java/jarupdater/MockMetadataDAO.java b/src/test/java/jarupdater/MockMetadataDAO.java new file mode 100644 index 0000000..82dd3e2 --- /dev/null +++ b/src/test/java/jarupdater/MockMetadataDAO.java @@ -0,0 +1,43 @@ +package jarupdater; + +import java.io.IOException; +import java.nio.file.NoSuchFileException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import eu.m724.jarupdater.live.MetadataDAO; +import eu.m724.jarupdater.object.Version; + +public class MockMetadataDAO implements MetadataDAO { + + @Override + public CompletableFuture> getChannels() { + List channels = Arrays.asList("stable", "beta", "alpha"); + return CompletableFuture.completedFuture(channels); + } + + @Override + public CompletableFuture getMetadata(String channel, String versionLabel) { + Version version = null; + String fileUrl = "http://127.0.0.1:17357/%s/%s.jar".formatted(channel, versionLabel); + + switch (versionLabel) { + case "1.0": + version = new Version(1, 100, "1.0", fileUrl, "dd3822ed965b2820aa0800f04b86f26d0b656fca3b97ddd264046076f81e3a24"); + break; + case "1.1": + version = new Version(2, 200, "1.1", fileUrl, "4d59994f607b89987d5bff430a4229edbbb045b3da9eaf1c63fa8e5fb208d824"); + break; + case "latest": + version = new Version(2, 200, "1.1", fileUrl, "4d59994f607b89987d5bff430a4229edbbb045b3da9eaf1c63fa8e5fb208d824"); + break; + default: + return CompletableFuture.failedFuture(new CompletionException(new IOException("no such version"))); + } + + return CompletableFuture.completedFuture(version); + } + +}