From 57d9127ed34a7895124e18e99fe10c162fb5c10b Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Sun, 15 Oct 2023 21:03:39 +0200 Subject: [PATCH 1/6] Use CompletableFutures for async update checking --- .../java/com/technicjelle/UpdateChecker.java | 89 ++++++++++--------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/technicjelle/UpdateChecker.java b/src/main/java/com/technicjelle/UpdateChecker.java index 21503ce..fa4473b 100644 --- a/src/main/java/com/technicjelle/UpdateChecker.java +++ b/src/main/java/com/technicjelle/UpdateChecker.java @@ -7,18 +7,20 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.MalformedURLException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; import java.util.logging.Logger; /** * Checks for updates on a GitHub repository */ public class UpdateChecker { - private final String repoName; + private final String currentVersion; private final URL url; - private boolean updateAvailable = false; - private String latestVersion = null; + private transient CompletableFuture latestVersionFuture = null; /** * @param author GitHub Username @@ -26,7 +28,6 @@ public class UpdateChecker { * @param currentVersion Current version of the program. This must be in the same format as the version tags on GitHub */ public UpdateChecker(@NotNull String author, @NotNull String repoName, @NotNull String currentVersion) { - this.repoName = repoName; this.currentVersion = removePrefix(currentVersion); try { this.url = new URL("https://github.com/" + author + "/" + repoName + "/releases/latest"); @@ -39,28 +40,10 @@ public UpdateChecker(@NotNull String author, @NotNull String repoName, @NotNull * Checks for updates from a GitHub repository's releases
* This method blocks the thread it is called from * - * @throws IOException If an IO exception occurs * @see #checkAsync() */ - public void check() throws IOException { - // Connect to GitHub website - HttpURLConnection con; - con = (HttpURLConnection) url.openConnection(); - con.setInstanceFollowRedirects(false); - - // Check if the response is a redirect - String newUrl = con.getHeaderField("Location"); - - if (newUrl == null) { - throw new IOException("Did not get a redirect"); - } - - // Get the latest version tag from the redirect url - String[] split = newUrl.split("/"); - latestVersion = removePrefix(split[split.length - 1]); - - // Check if the latest version is not the current version - if (!latestVersion.equals(currentVersion)) updateAvailable = true; + public void check() { + getLatestVersion(); } /** @@ -70,36 +53,56 @@ public void check() throws IOException { * @see #check() */ public void checkAsync() { - String s = System.getProperty("technicjelle.updatechecker.noasync"); + latestVersionFuture = CompletableFuture.supplyAsync(this::fetchLatestVersion); + } - if (s != null) { - try { - check(); - } catch (IOException e) { - throw new RuntimeException(e); + /** + * Checks if necessary and returns the latest available version + * @return the latest available version + */ + public synchronized String getLatestVersion() { + if (latestVersionFuture == null) checkAsync(); + return latestVersionFuture.join(); + } + + private String fetchLatestVersion() { + try { + // Connect to GitHub website + HttpURLConnection con; + con = (HttpURLConnection) url.openConnection(); + con.setInstanceFollowRedirects(false); + + // Check if the response is a redirect + String newUrl = con.getHeaderField("Location"); + + if (newUrl == null) { + throw new IOException("Did not get a redirect"); } - return; + + // Get the latest version tag from the redirect url + String[] split = newUrl.split("/"); + return removePrefix(split[split.length - 1]); + } catch (IOException ex) { + throw new CompletionException("Exception trying to fetch the latest version", ex); } + } - new Thread(() -> { - try { - check(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }, repoName + "-Update-Checker").start(); + /** + * Checks if necessary and returns weather an update is available or not + * @return true if there is an update available or false otherwise. + */ + public boolean isUpdateAvailable() { + return !getLatestVersion().equals(currentVersion); } /** * This method logs a message to the console if an update is available
* * @param logger Logger to log a potential update notification to - * @throws IllegalStateException If {@link #check()} has not been called */ - public void logUpdateMessage(@NotNull Logger logger) throws IllegalStateException { - if (latestVersion == null) throw new IllegalStateException("check() has not been called"); - if (updateAvailable) { - logger.warning("New version available: v" + latestVersion + " (current: v" + currentVersion + ")"); + public void logUpdateMessage(@NotNull Logger logger) { + if (isUpdateAvailable()) { + logger.warning("New version available: v" + getLatestVersion() + " (current: v" + currentVersion + ")"); logger.warning("Download it at " + url); } } From ae1d3f2623496286018212324a42bd5dc247c433 Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Sun, 15 Oct 2023 21:12:53 +0200 Subject: [PATCH 2/6] Fix check() not actually forcing a new check --- src/main/java/com/technicjelle/UpdateChecker.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/technicjelle/UpdateChecker.java b/src/main/java/com/technicjelle/UpdateChecker.java index fa4473b..09ae7ae 100644 --- a/src/main/java/com/technicjelle/UpdateChecker.java +++ b/src/main/java/com/technicjelle/UpdateChecker.java @@ -43,7 +43,8 @@ public UpdateChecker(@NotNull String author, @NotNull String repoName, @NotNull * @see #checkAsync() */ public void check() { - getLatestVersion(); + checkAsync(); + latestVersionFuture.join(); } /** From a40c17a5d6bf2d17ad6e7b7c2bc7d7d03e61dfa2 Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Sun, 15 Oct 2023 21:14:23 +0200 Subject: [PATCH 3/6] Fix javadoc --- src/main/java/com/technicjelle/UpdateChecker.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/technicjelle/UpdateChecker.java b/src/main/java/com/technicjelle/UpdateChecker.java index 09ae7ae..9cc0e75 100644 --- a/src/main/java/com/technicjelle/UpdateChecker.java +++ b/src/main/java/com/technicjelle/UpdateChecker.java @@ -49,8 +49,7 @@ public void check() { /** * Checks for updates from a GitHub repository's releases
- * This method does not block the thread it is called from
- *
Start your program with -Dtechnicjelle.updatechecker.noasync to disable async, and just always check synchronously + * This method does not block the thread it is called from * @see #check() */ public void checkAsync() { From 39db9ba6975e5ce48ef3336d9036d2845873f8b0 Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Sun, 15 Oct 2023 21:28:23 +0200 Subject: [PATCH 4/6] Add logUpdateMessageAsync() for easy async checking + logging as soon as the check is done --- src/main/java/com/technicjelle/UpdateChecker.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/technicjelle/UpdateChecker.java b/src/main/java/com/technicjelle/UpdateChecker.java index 9cc0e75..af81abb 100644 --- a/src/main/java/com/technicjelle/UpdateChecker.java +++ b/src/main/java/com/technicjelle/UpdateChecker.java @@ -107,6 +107,16 @@ public void logUpdateMessage(@NotNull Logger logger) { } } + /** + * This method logs a message to the console if an update is available, asynchronously
+ * + * @param logger Logger to log a potential update notification to + */ + public synchronized void logUpdateMessageAsync(@NotNull Logger logger) { + if (latestVersionFuture == null) checkAsync(); + latestVersionFuture.thenRun(() -> logUpdateMessage(logger)); + } + /** * Removes a potential v prefix from a version * From add05d780d44f5c9eafc672f105d776b2dd1ada8 Mon Sep 17 00:00:00 2001 From: "Lukas Rieger (Blue)" Date: Sun, 22 Oct 2023 22:49:58 +0200 Subject: [PATCH 5/6] Organize imports --- src/main/java/com/technicjelle/UpdateChecker.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/technicjelle/UpdateChecker.java b/src/main/java/com/technicjelle/UpdateChecker.java index af81abb..e56efd2 100644 --- a/src/main/java/com/technicjelle/UpdateChecker.java +++ b/src/main/java/com/technicjelle/UpdateChecker.java @@ -5,11 +5,10 @@ import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URL; import java.net.MalformedURLException; +import java.net.URL; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; import java.util.logging.Logger; /** From 78bd65b649e13ccd2782d60cecfd346be16a42b9 Mon Sep 17 00:00:00 2001 From: TechnicJelle <22576047+TechnicJelle@users.noreply.github.com> Date: Sat, 4 Nov 2023 00:26:42 +0100 Subject: [PATCH 6/6] Allow users to override the async to be sync --- README.md | 6 +----- src/main/java/com/technicjelle/UpdateChecker.java | 5 ++++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6e01d61..ee4c3c1 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Simply instantiate a new `UpdateChecker` object with your GitHub username, repos Then call `.check()` or `.checkAsync()` on the instance to check for updates. -You can then log the update message with `logUpdateMessage()`. +You can then log the update message with `logUpdateMessage()` or `logUpdateMessageAsync()`. ```java UpdateChecker updateChecker = new UpdateChecker("TechnicJelle", "UpdateCheckerJava", "2.0"); @@ -18,7 +18,3 @@ updateChecker.logUpdateMessage(getLogger()); ``` Full javadoc API reference: [technicjelle.com/UpdateCheckerJava](https://technicjelle.com/UpdateCheckerJava/com/technicjelle/UpdateChecker.html) - -When using the async method, you, or your program's users, can override it to be synchronous anyway, -by passing `-Dtechnicjelle.updatechecker.noasync` as a JVM argument.\ -Example: `java -Dtechnicjelle.updatechecker.noasync -jar server.jar` diff --git a/src/main/java/com/technicjelle/UpdateChecker.java b/src/main/java/com/technicjelle/UpdateChecker.java index e56efd2..a29cd6d 100644 --- a/src/main/java/com/technicjelle/UpdateChecker.java +++ b/src/main/java/com/technicjelle/UpdateChecker.java @@ -49,6 +49,7 @@ public void check() { /** * Checks for updates from a GitHub repository's releases
* This method does not block the thread it is called from + * * @see #check() */ public void checkAsync() { @@ -57,6 +58,7 @@ public void checkAsync() { /** * Checks if necessary and returns the latest available version + * * @return the latest available version */ public synchronized String getLatestVersion() { @@ -87,7 +89,8 @@ private String fetchLatestVersion() { } /** - * Checks if necessary and returns weather an update is available or not + * Checks if necessary and returns whether an update is available or not + * * @return true if there is an update available or false otherwise. */ public boolean isUpdateAvailable() {