fnihs work

im so tired rn
This commit is contained in:
Minecon724 2024-06-25 14:26:36 +02:00
parent 9263818e3e
commit 34a3ee1d38
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
14 changed files with 365 additions and 10 deletions

15
pom.xml
View file

@ -16,6 +16,21 @@
<url>https://git.724.rocks/api/packages/Minecon724/maven</url> <url>https://git.724.rocks/api/packages/Minecon724/maven</url>
</repository> </repository>
</repositories> </repositories>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
<optional>true</optional> <!-- only for giteametadatadao -->
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build> <build>
<plugins> <plugins>

View file

@ -1,8 +1,11 @@
package eu.m724.jarupdater; package eu.m724.jarupdater;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.NoSuchFileException; import java.nio.file.NoSuchFileException;
import java.security.SignatureException;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import eu.m724.jarupdater.download.Downloader; import eu.m724.jarupdater.download.Downloader;
import eu.m724.jarupdater.environment.Environment; import eu.m724.jarupdater.environment.Environment;
@ -26,6 +29,10 @@ public class Updater {
return environment; return environment;
} }
/**
* get the latest available version
* @return a future which can throw ioexpception
*/
public CompletableFuture<Version> getLatestVersion() { public CompletableFuture<Version> getLatestVersion() {
CompletableFuture<Version> currentVersionFuture = metadataProvider.getCurrentVersionMetadata(); CompletableFuture<Version> currentVersionFuture = metadataProvider.getCurrentVersionMetadata();
CompletableFuture<Version> latestVersionFuture = metadataProvider.getLatestVersionMetadata(); CompletableFuture<Version> latestVersionFuture = metadataProvider.getLatestVersionMetadata();
@ -34,14 +41,25 @@ public class Updater {
Version currentVersion = currentVersionFuture.join(); Version currentVersion = currentVersionFuture.join();
Version latestVersion = latestVersionFuture.join(); Version latestVersion = latestVersionFuture.join();
if (currentVersion == null || latestVersion == null)
throw new CompletionException(new IOException());
return latestVersion.getId() > currentVersion.getId() ? latestVersion : null; return latestVersion.getId() > currentVersion.getId() ? latestVersion : null;
}); });
} }
/**
* get information about this version
* @return a future which can throw ioexpception
*/
public CompletableFuture<Version> getCurrentVersion() { public CompletableFuture<Version> getCurrentVersion() {
return metadataProvider.getCurrentVersionMetadata(); 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<File> downloadLatestVersion() { public CompletableFuture<File> downloadLatestVersion() {
CompletableFuture<Version> latestVersionFuture = metadataProvider.getLatestVersionMetadata(); CompletableFuture<Version> latestVersionFuture = metadataProvider.getLatestVersionMetadata();

View file

@ -4,6 +4,19 @@ import java.io.File;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public interface Downloader { public interface Downloader {
/**
* downloads a file and verifies it
* @param url
* @param sha256hex
* @return a future which can throw ioexception or signatureexception
*/
public CompletableFuture<File> downloadAndVerify(String url, String sha256hex); public CompletableFuture<File> downloadAndVerify(String url, String sha256hex);
/**
* moves source into destination
* @param source
* @param destination
* @return a future which can throw ioexception
*/
public CompletableFuture<Void> install(File source, File destination); public CompletableFuture<Void> install(File source, File destination);
} }

View file

@ -64,7 +64,7 @@ public class SimpleDownloader implements Downloader {
return downloadedFile; return downloadedFile;
} catch (IOException | NoSuchAlgorithmException | SignatureException e) { } catch (IOException | NoSuchAlgorithmException | SignatureException e) { // TODO nosuchalgo should not be thrown
throw new CompletionException(e); throw new CompletionException(e);
} }

View file

@ -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<List<String>> getChannels() {
String url = getFileUrl("channels.txt");
CompletableFuture<List<String>> 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<Version> getMetadata(String channel, String versionLabel) {
String url = getFileUrl(versionLabel, "meta-v1.json");
CompletableFuture<Version> 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<HttpResponse<String>> makeRequest(String url) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("User-Agent", "ju/1") // jar updater v1
.build();
CompletableFuture<HttpResponse<String>> 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);
}
}

View file

@ -1,11 +1,22 @@
package eu.m724.jarupdater.live; package eu.m724.jarupdater.live;
import java.util.ArrayList; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import eu.m724.jarupdater.object.Version; import eu.m724.jarupdater.object.Version;
public interface MetadataDAO { public interface MetadataDAO {
public CompletableFuture<ArrayList<String>> getChannels(); /**
public CompletableFuture<Version> getMetadata(String channel, String version); * get available channels online
* @return a future which can throw ioexception
*/
public CompletableFuture<List<String>> getChannels();
/**
* get metadata of a version in channel
* @param channel
* @param versionLabel
* @return a future which can throw ioexcpeitons or nosuchfilexxception
*/
public CompletableFuture<Version> getMetadata(String channel, String versionLabel);
} }

View file

@ -1,7 +1,7 @@
package eu.m724.jarupdater.live; package eu.m724.jarupdater.live;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import eu.m724.jarupdater.environment.Environment; import eu.m724.jarupdater.environment.Environment;
@ -12,14 +12,14 @@ public class MetadataFacade {
private MetadataDAO metadataDao; private MetadataDAO metadataDao;
private HashMap<String, CompletableFuture<Version>> cache = new HashMap<>(); private HashMap<String, CompletableFuture<Version>> cache = new HashMap<>();
private CompletableFuture<ArrayList<String>> channels = null; private CompletableFuture<List<String>> channels = null;
public MetadataFacade(Environment environment, MetadataDAO metadataDao) { public MetadataFacade(Environment environment, MetadataDAO metadataDao) {
this.environment = environment; this.environment = environment;
this.metadataDao = metadataDao; this.metadataDao = metadataDao;
} }
public CompletableFuture<ArrayList<String>> getChannels() { public CompletableFuture<List<String>> getChannels() {
if (channels == null) if (channels == null)
channels = metadataDao.getChannels(); channels = metadataDao.getChannels();

View file

@ -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);
}
}

View file

@ -5,7 +5,15 @@ public class Version {
* metadata file version * metadata file version
*/ */
public static final int SPEC = 1; 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 int id;
private long timestamp; private long timestamp;
private String label; private String label;

View file

@ -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;
}
}
}

View file

@ -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"));
}
}

View file

@ -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;
}
}
}

View file

@ -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<File> downloadAndVerify(String url, String sha256hex) {
CompletableFuture<File> 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<Void> install(File source, File destination) {
CompletableFuture<Void> future = null;
if (source.getName().equals("ionlyexistfortesting"))
future = CompletableFuture.completedFuture(null);
else
future = CompletableFuture.failedFuture(new CompletionException(new IOException()));
return future;
}
}

View file

@ -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<List<String>> getChannels() {
List<String> channels = Arrays.asList("stable", "beta", "alpha");
return CompletableFuture.completedFuture(channels);
}
@Override
public CompletableFuture<Version> 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);
}
}