diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..edfbc0b --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* joshua@hypera.dev \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7957476 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" \ No newline at end of file diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index e054d30..937b596 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -10,7 +10,7 @@ jobs: - name: Set up Java uses: actions/setup-java@v2 with: - java-version: '8' + java-version: '11' distribution: 'adopt' server-id: hypera-releases server-username: MAVEN_USERNAME diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml deleted file mode 100644 index 58937fe..0000000 --- a/.github/workflows/publish-snapshot.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Publish Snapshot -on: - push: - branches: [main] -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Java - uses: actions/setup-java@v2 - with: - java-version: '8' - distribution: 'adopt' - server-id: hypera-snapshots - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - - name: Publish package - run: mvn --batch-mode deploy - env: - MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.MAVEN_TOKEN }} diff --git a/SECURITY.md b/SECURITY.md index 7b526f9..363dd7c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,6 +4,7 @@ | Version | Supported | | :------ | -----------------: | +| 3.x.x | :white_check_mark: | | 2.0.x | :white_check_mark: | | < 2.0 | :x: | diff --git a/pom.xml b/pom.xml index 50c81d1..b018940 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ dev.hypera UpdateLib - 2.1.2 + 3.1.2-SNAPSHOT jar UpdateLib @@ -113,7 +113,7 @@ - + com.googlecode.json-simple json-simple @@ -126,13 +126,6 @@ - - - - org.apache.maven - maven-artifact - 3.6.3 - \ No newline at end of file diff --git a/src/main/java/dev/hypera/updatelib/UpdateLib.java b/src/main/java/dev/hypera/updatelib/UpdateLib.java new file mode 100644 index 0000000..a9fab12 --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/UpdateLib.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib; + +import dev.hypera.updatelib.annotations.RunAsync; +import dev.hypera.updatelib.checkers.UpdateChecker; +import dev.hypera.updatelib.data.CheckData; +import dev.hypera.updatelib.objects.UpdateStatus; +import dev.hypera.updatelib.objects.UpdateStatusBuilder; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.function.Consumer; + +public class UpdateLib { + + private final static String VERSION = "3.1.2-SNAPSHOT"; // Current UpdateLib version. + + private final long resourceId; + private final String currentVersion; + private final int connectionTimeout; + + private final UpdateChecker updateChecker; + + private final Consumer completeAction; + private final Consumer errorHandler; + + private UpdateStatus lastStatus = null; + private long lastCheck = 0L; + + /** + * UpdateLib Constructor - Used internally by UpdateLib. + * + * @param resourceId Resource ID. + * @param currentVersion Current version of the Resource. + * @param repeatingChecksEnabled If UpdateLib should check for updates periodically. + * @param interval How often UpdateLib should check for updates. + * @param connectionTimeout The amount of milliseconds UpdateLib should wait before timing out on requests. + * @param updateChecker {@link UpdateChecker} to be used by UpdateLib to check for updates. + * @param completeAction Action to run after UpdateLib has checked for an update. + * @param errorHandler Consumer to run if UpdateLib encounters an exception. + */ + protected UpdateLib(long resourceId, String currentVersion, boolean repeatingChecksEnabled, long interval, int connectionTimeout, UpdateChecker updateChecker, Consumer completeAction, Consumer errorHandler) { + this.resourceId = resourceId; + this.currentVersion = currentVersion; + this.connectionTimeout = connectionTimeout; + this.updateChecker = updateChecker; + this.completeAction = completeAction; + this.errorHandler = errorHandler; + + Thread thread = new Thread(this::checkNow); + thread.setName("UpdateLib-" + thread.getId()); + thread.start(); + + if(repeatingChecksEnabled) { + new Timer().schedule(new TimerTask() { + @Override + public void run() { + checkNow(); + } + }, interval); + } + } + + + /** + * Start an update check. It's recommended to only run this asynchronously as it may take time to fetch data from + * the API. + * + * @return Response - Instance of {@link UpdateStatus} + * @since 2.0.0-SNAPSHOT + */ + @RunAsync + public UpdateStatus checkNow() { + try { + lastStatus = updateChecker.check(new CheckData(resourceId, currentVersion, connectionTimeout)); + lastCheck = System.currentTimeMillis(); + if(null != completeAction) + completeAction.accept(lastStatus); + } catch (Exception ex) { + if(null == errorHandler) + ex.printStackTrace(); + else + errorHandler.accept(ex); + } + + return lastStatus; + } + + /** + * Get last stored {@link UpdateStatus}. If UpdateLib hasn't checked for an update or the last check failed, this + * will return null. + * + * @return Last stored {@link UpdateStatus} + * @since 3.0.0-SNAPSHOT + */ + public UpdateStatus getLastStatus() { + return lastStatus; + } + + /** + * Get the last time UpdateLib checked for updates. + * + * @return Last check time in milliseconds. + * @since 2.0.0-SNAPSHOT + */ + public long getLastCheck() { + return lastCheck; + } + + /** + * Get the current version of UpdateLib. + * + * @return Current version of UpdateLib. + * @since 2.1.0-SNAPSHOT + */ + public static String getVersion() { + return VERSION; + } + +} diff --git a/src/main/java/dev/hypera/updatelib/UpdateLibBuilder.java b/src/main/java/dev/hypera/updatelib/UpdateLibBuilder.java index 03002d4..625fa06 100644 --- a/src/main/java/dev/hypera/updatelib/UpdateLibBuilder.java +++ b/src/main/java/dev/hypera/updatelib/UpdateLibBuilder.java @@ -16,33 +16,51 @@ package dev.hypera.updatelib; -import dev.hypera.updatelib.internal.UpdateLib; -import dev.hypera.updatelib.internal.UpdateResponse; +import dev.hypera.updatelib.annotations.Builder; +import dev.hypera.updatelib.checkers.UpdateChecker; +import dev.hypera.updatelib.checkers.impl.SpigotUpdateChecker; +import dev.hypera.updatelib.objects.UpdateStatus; +import dev.hypera.updatelib.utils.Check; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -@SuppressWarnings("unused") +@Builder public class UpdateLibBuilder { private final long resourceId; private final String currentVersion; private boolean repeatingChecksEnabled = true; - private long checkInterval = 2 * (60 * (60 * 1000)); // 2 Hours - private int connectionTimeout = 5000; // 5 Seconds + private long checkInterval = 2 * (60 * (60 * 1000)); // Defaults to 2 hours. + private int connectionTimeout = 10000; // Defaults to 10 seconds. - private Consumer consumer = null; + /** + * Defaults to using {@link SpigotUpdateChecker}. + */ + private UpdateChecker updateChecker = new SpigotUpdateChecker(); + + private Consumer completeAction = null; + private Consumer errorHandler = null; // If error handler is not provided, stack traces will be printed by UpdateLib. + + /** + * UpdateLibBuilder Constructor - Used internally by UpdateLib. + * + * @param currentVersion Current version of the resource. + * @param resourceId Resource ID. + */ private UpdateLibBuilder(String currentVersion, long resourceId) { + Check.notNull("currentVersion", currentVersion); this.currentVersion = currentVersion; this.resourceId = resourceId; } /** - * Creates a new instance of {@link UpdateLibBuilder}. + * Create a new instance of {@link UpdateLibBuilder} * * @param currentVersion Current version of the resource. - * @param resourceId SpigotMC Resource Id. + * @param resourceId Resource ID. * * @return Instance of {@link UpdateLibBuilder} * @since 2.0.0-SNAPSHOT @@ -52,11 +70,11 @@ public static UpdateLibBuilder create(String currentVersion, long resourceId) { } /** - * Should UpdateLib keep checking for updates? (Time defined by checkInterval) + * Sets if UpdateLib should check for updates periodically. * - * @param enabled Repeating checks enabled. + * @param enabled If UpdateLib should check for updates periodically. * - * @see #setCheckInterval(long) + * @return {@link UpdateLibBuilder} * @since 2.0.0-SNAPSHOT */ public UpdateLibBuilder setRepeatingChecksEnabled(boolean enabled) { @@ -65,23 +83,26 @@ public UpdateLibBuilder setRepeatingChecksEnabled(boolean enabled) { } /** - * How often should UpdateLib check for updates? (Only works if repeatingChecksEnabled is true) + * Sets how often UpdateLib should check for updates? This is only needed if 'repeatingChecksEnabled' is true. * - * @param interval Interval in milliseconds. + * @param time Time. + * @param unit TimeUnit. * + * @return {@link UpdateLibBuilder} * @see #setRepeatingChecksEnabled(boolean) * @since 2.0.0-SNAPSHOT */ - public UpdateLibBuilder setCheckInterval(long interval) { - this.checkInterval = interval; + public UpdateLibBuilder setCheckInterval(long time, TimeUnit unit) { + this.checkInterval = unit.toMillis(time); return this; } /** - * After how many milliseconds should we timeout the request to SpigotMC's API? + * Sets the amount of milliseconds UpdateLib should wait before timing out on requests. * * @param timeout Timeout in milliseconds. * + * @return {@link UpdateLibBuilder} * @since 2.0.0-SNAPSHOT */ public UpdateLibBuilder setConnectionTimeout(int timeout) { @@ -90,25 +111,52 @@ public UpdateLibBuilder setConnectionTimeout(int timeout) { } /** - * Sets a consumer to run after UpdateLib has checked for an update. + * Sets the {@link UpdateChecker} to be used by UpdateLib to check for updates. + * + * @param updateChecker Instance of an {@link UpdateChecker} + * + * @return {@link UpdateLibBuilder} + * @since 3.0.0-SNAPSHOT + */ + public UpdateLibBuilder setUpdateChecker(UpdateChecker updateChecker) { + this.updateChecker = updateChecker; + return this; + } + + /** + * Sets an action to run after UpdateLib has checked for an update. * - * @param consumer Consumer to run after checking for an update. + * @param action Consumer to run after checking for an update. * + * @return {@link UpdateLibBuilder} * @since 2.1.0-SNAPSHOT */ - public UpdateLibBuilder setConsumer(Consumer consumer) { - this.consumer = consumer; + public UpdateLibBuilder setCompleteAction(Consumer action) { + this.completeAction = action; + return this; + } + + /** + * Sets an consumer to run if UpdateLib encounters an exception. + * + * @param handler Error handler. + * + * @return {@link UpdateLibBuilder} + * @since 3.0.0-SNAPSHOT + */ + public UpdateLibBuilder setErrorHandler(Consumer handler) { + this.errorHandler = handler; return this; } /** - * Builds a new instance of {@link UpdateLib}. + * Builds a new instance of {@link UpdateLib} * * @return Instance of {@link UpdateLib} * @since 2.0.0-SNAPSHOT */ public UpdateLib build() { - return new UpdateLib(resourceId, currentVersion, repeatingChecksEnabled, checkInterval, connectionTimeout, consumer); + return new UpdateLib(resourceId, currentVersion, repeatingChecksEnabled, checkInterval, connectionTimeout, updateChecker, completeAction, errorHandler); } } diff --git a/src/main/java/dev/hypera/updatelib/annotations/Builder.java b/src/main/java/dev/hypera/updatelib/annotations/Builder.java new file mode 100644 index 0000000..2466e3f --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/annotations/Builder.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for marking a class as a Builder. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Builder { + +} diff --git a/src/main/java/dev/hypera/updatelib/annotations/Internal.java b/src/main/java/dev/hypera/updatelib/annotations/Internal.java new file mode 100644 index 0000000..2ca481b --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/annotations/Internal.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for marking a class as for use by UpdateLib only. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Internal { + +} diff --git a/src/main/java/dev/hypera/updatelib/annotations/Required.java b/src/main/java/dev/hypera/updatelib/annotations/Required.java new file mode 100644 index 0000000..6ee79b8 --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/annotations/Required.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation for marking something as being required. + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface Required { + +} diff --git a/src/main/java/dev/hypera/updatelib/annotations/RunAsync.java b/src/main/java/dev/hypera/updatelib/annotations/RunAsync.java new file mode 100644 index 0000000..4318e86 --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/annotations/RunAsync.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for marking something as needing to be run Asynchronously. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RunAsync { + +} diff --git a/src/main/java/dev/hypera/updatelib/checkers/UpdateChecker.java b/src/main/java/dev/hypera/updatelib/checkers/UpdateChecker.java new file mode 100644 index 0000000..f1cf763 --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/checkers/UpdateChecker.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.checkers; + +import dev.hypera.updatelib.data.CheckData; +import dev.hypera.updatelib.objects.UpdateStatus; + +public interface UpdateChecker { + + /** + * Check for an update. + * + * @param data Check data. + * @return {@link UpdateStatus} + * @throws Exception Any exceptions that occur while checking for updates. + * @since 3.0.0-SNAPSHOT + */ + UpdateStatus check(CheckData data) throws Exception; + +} diff --git a/src/main/java/dev/hypera/updatelib/checkers/impl/PolymartUpdateChecker.java b/src/main/java/dev/hypera/updatelib/checkers/impl/PolymartUpdateChecker.java new file mode 100644 index 0000000..2686271 --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/checkers/impl/PolymartUpdateChecker.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.checkers.impl; + +import dev.hypera.updatelib.checkers.UpdateChecker; +import dev.hypera.updatelib.data.CheckData; +import dev.hypera.updatelib.exceptions.InvalidResourceException; +import dev.hypera.updatelib.objects.UpdateStatus; +import dev.hypera.updatelib.objects.UpdateStatusBuilder; +import dev.hypera.updatelib.utils.ReaderUtils; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; + +public class PolymartUpdateChecker implements UpdateChecker { + + private static final String URL_FORMAT = "https://api.polymart.org/v1/getResourceInfo/?resource_id=%s"; + + /** + * Check for an update using Polymart's API. + * + * @param data Check data. + * @return {@link UpdateStatus} + * @throws Exception Any exceptions that occur while checking for updates. + * @since 3.0.0-SNAPSHOT + */ + @Override + public UpdateStatus check(CheckData data) throws Exception { + URL url = new URL(String.format(URL_FORMAT, data.getResourceId())); + + HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setConnectTimeout(data.getTimeout()); + httpsURLConnection.setReadTimeout(data.getTimeout()); + + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream())); + JSONObject json = (JSONObject) JSONValue.parse(ReaderUtils.readAll(bufferedReader)); + bufferedReader.close(); + + JSONObject response = (JSONObject) json.get("response"); + if(!((boolean) response.get("success"))) + throw new InvalidResourceException("Cannot find Polymart resource with id '" + data.getResourceId() + "'"); + + String distributedVersion = ((JSONObject) ((JSONObject) ((JSONObject) response.get("resource")).get("updates")).get("latest")).get("version").toString(); + + return UpdateStatusBuilder.create(distributedVersion, data.getCurrentVersion()).build(); + } + +} diff --git a/src/main/java/dev/hypera/updatelib/checkers/impl/SongodaUpdateChecker.java b/src/main/java/dev/hypera/updatelib/checkers/impl/SongodaUpdateChecker.java new file mode 100644 index 0000000..94eb9ad --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/checkers/impl/SongodaUpdateChecker.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.checkers.impl; + +import dev.hypera.updatelib.checkers.UpdateChecker; +import dev.hypera.updatelib.data.CheckData; +import dev.hypera.updatelib.exceptions.InvalidResourceException; +import dev.hypera.updatelib.objects.UpdateStatus; +import dev.hypera.updatelib.objects.UpdateStatusBuilder; +import dev.hypera.updatelib.utils.ReaderUtils; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; + +public class SongodaUpdateChecker implements UpdateChecker { + + private static final String URL_FORMAT = "https://songoda.com/api/v2/products/id/%s"; + + /** + * Check for an update using Songoda's API. + * + * @param data Check data. + * + * @return {@link UpdateStatus} + * @throws Exception Any exceptions that occur while checking for updates. + * @since 3.1.0-SNAPSHOT + */ + @Override + public UpdateStatus check(CheckData data) throws Exception { + URL url = new URL(String.format(URL_FORMAT, data.getResourceId())); + + HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setConnectTimeout(data.getTimeout()); + httpsURLConnection.setReadTimeout(data.getTimeout()); + + if(httpsURLConnection.getResponseCode() == 404) + throw new InvalidResourceException("Cannot find Songoda resource with id '" + data.getResourceId() + "'"); + + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream())); + JSONObject json = (JSONObject) JSONValue.parse(ReaderUtils.readAll(bufferedReader)); + bufferedReader.close(); + + JSONObject songodaData = (JSONObject) json.get("data"); + String distributedVersion = ((JSONObject) ((JSONArray) songodaData.get("versions")).get(0)).get("version").toString(); + + return UpdateStatusBuilder.create(distributedVersion, data.getCurrentVersion()).build(); + } + +} diff --git a/src/main/java/dev/hypera/updatelib/checkers/impl/SpigotLegacyUpdateChecker.java b/src/main/java/dev/hypera/updatelib/checkers/impl/SpigotLegacyUpdateChecker.java new file mode 100644 index 0000000..a055b59 --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/checkers/impl/SpigotLegacyUpdateChecker.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.checkers.impl; + +import dev.hypera.updatelib.checkers.UpdateChecker; +import dev.hypera.updatelib.data.CheckData; +import dev.hypera.updatelib.exceptions.InvalidResourceException; +import dev.hypera.updatelib.objects.UpdateStatus; +import dev.hypera.updatelib.objects.UpdateStatusBuilder; +import dev.hypera.updatelib.utils.ReaderUtils; + +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; + +public class SpigotLegacyUpdateChecker implements UpdateChecker { + + private static final String URL_FORMAT = "https://api.spigotmc.org/legacy/update.php?resource=%s"; + + /** + * Check for an update using SpigotMC's Legacy API. + * + * @param data Check data. + * @return {@link UpdateStatus} + * @throws Exception Any exceptions that occur while checking for updates. + * @since 3.0.0-SNAPSHOT + */ + @Override + public UpdateStatus check(CheckData data) throws Exception { + URL url = new URL(String.format(URL_FORMAT, data.getResourceId())); + + HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setConnectTimeout(data.getTimeout()); + httpsURLConnection.setReadTimeout(data.getTimeout()); + + if(httpsURLConnection.getResponseCode() == 404) + throw new InvalidResourceException("Cannot find SpigotMC resource with id '" + data.getResourceId() + "'"); + + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream())); + String distributedVersion = ReaderUtils.readAll(bufferedReader); + bufferedReader.close(); + + if(distributedVersion.contains("Invalid")) + throw new InvalidResourceException("Cannot find SpigotMC resource with id '" + data.getResourceId() + "'"); + + return UpdateStatusBuilder.create(distributedVersion, data.getCurrentVersion()).build(); + } + +} diff --git a/src/main/java/dev/hypera/updatelib/checkers/impl/SpigotUpdateChecker.java b/src/main/java/dev/hypera/updatelib/checkers/impl/SpigotUpdateChecker.java new file mode 100644 index 0000000..298d3d8 --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/checkers/impl/SpigotUpdateChecker.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.checkers.impl; + +import dev.hypera.updatelib.checkers.UpdateChecker; +import dev.hypera.updatelib.data.CheckData; +import dev.hypera.updatelib.exceptions.InvalidResourceException; +import dev.hypera.updatelib.objects.UpdateStatus; +import dev.hypera.updatelib.objects.UpdateStatusBuilder; +import dev.hypera.updatelib.utils.ReaderUtils; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; + +public class SpigotUpdateChecker implements UpdateChecker { + + private static final String URL_FORMAT = "https://api.spigotmc.org/simple/0.1/index.php?action=getResource&id=%s"; + + /** + * Check for an update using SpigotMC's API. + * + * @param data Check data. + * @return {@link UpdateStatus} + * @throws Exception Any exceptions that occur while checking for updates. + * @since 3.0.0-SNAPSHOT + */ + @Override + public UpdateStatus check(CheckData data) throws Exception { + URL url = new URL(String.format(URL_FORMAT, data.getResourceId())); + + HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection(); + httpsURLConnection.setConnectTimeout(data.getTimeout()); + httpsURLConnection.setReadTimeout(data.getTimeout()); + + if(httpsURLConnection.getResponseCode() == 404) + throw new InvalidResourceException("Cannot find SpigotMC resource with id '" + data.getResourceId() + "'"); + + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream())); + JSONObject json = (JSONObject) JSONValue.parse(ReaderUtils.readAll(bufferedReader)); + bufferedReader.close(); + + String distributedVersion = json.get("current_version").toString(); + + return UpdateStatusBuilder.create(distributedVersion, data.getCurrentVersion()).build(); + } + +} diff --git a/src/main/java/dev/hypera/updatelib/internal/UpdateResponse.java b/src/main/java/dev/hypera/updatelib/data/CheckData.java similarity index 53% rename from src/main/java/dev/hypera/updatelib/internal/UpdateResponse.java rename to src/main/java/dev/hypera/updatelib/data/CheckData.java index a681325..6f6b388 100644 --- a/src/main/java/dev/hypera/updatelib/internal/UpdateResponse.java +++ b/src/main/java/dev/hypera/updatelib/data/CheckData.java @@ -14,48 +14,61 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -package dev.hypera.updatelib.internal; +package dev.hypera.updatelib.data; -public class UpdateResponse { +import dev.hypera.updatelib.annotations.Internal; - private final boolean updateAvailable; +@Internal +public class CheckData { + + private final long resourceId; private final String currentVersion; - private final String spigotVersion; + private final int timeout; - public UpdateResponse(boolean updateAvailable, String currentVersion, String spigotVersion) { - this.updateAvailable = updateAvailable; + /** + * CheckData Constructor - Used internally by UpdateLib. + * + * @param resourceId Resource ID. + * @param currentVersion Current resource version. + * @param timeout Connection timeout. + * + * @since 3.0.0-SNAPSHOT + */ + public CheckData(long resourceId, String currentVersion, int timeout) { + this.resourceId = resourceId; this.currentVersion = currentVersion; - this.spigotVersion = spigotVersion; + this.timeout = timeout; } /** - * Is an update available? + * Get resource ID. * - * @return Update available. - * @since 2.0.0-SNAPSHOT + * @return Resource ID. + * @since 3.0.0-SNAPSHOT */ - public boolean isUpdateAvailable() { - return updateAvailable; + public long getResourceId() { + return resourceId; } /** - * Get the current version of the plugin. + * Get current version. * * @return Current version. - * @since 2.0.0-SNAPSHOT + * @since 3.0.0-SNAPSHOT */ public String getCurrentVersion() { return currentVersion; } /** - * Get the current version of the plugin on SpigotMC. + * Get connection timeout. * - * @return Current SpigotMC version. - * @since 2.0.0-SNAPSHOT + * @return Connection timeout in milliseconds. + * @since 3.0.0-SNAPSHOT */ - public String getSpigotVersion() { - return spigotVersion; + public int getTimeout() { + return timeout; } + } diff --git a/src/main/java/dev/hypera/updatelib/exceptions/InvalidResourceException.java b/src/main/java/dev/hypera/updatelib/exceptions/InvalidResourceException.java new file mode 100644 index 0000000..37dd473 --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/exceptions/InvalidResourceException.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.exceptions; + +import java.io.IOException; + +/** + * Invalid Resource Exception. + * + * @since 3.0.0-SNAPSHOT + */ +public class InvalidResourceException extends IOException { + + public InvalidResourceException() { + super(); + } + + public InvalidResourceException(String s) { + super(s); + } + + public InvalidResourceException(String s, Throwable throwable) { + super(s, throwable); + } + + public InvalidResourceException(Throwable throwable) { + super(throwable); + } + +} diff --git a/src/main/java/dev/hypera/updatelib/exceptions/VersionSchemeException.java b/src/main/java/dev/hypera/updatelib/exceptions/VersionSchemeException.java new file mode 100644 index 0000000..dc54e5c --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/exceptions/VersionSchemeException.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.exceptions; + +/** + * Version Scheme Exception. + * + * @since 3.0.0-SNAPSHOT + */ +public class VersionSchemeException extends IllegalStateException { + + public VersionSchemeException() { + super(); + } + + public VersionSchemeException(String s) { + super(s); + } + + public VersionSchemeException(String s, Throwable throwable) { + super(s, throwable); + } + + public VersionSchemeException(Throwable throwable) { + super(throwable); + } + +} diff --git a/src/main/java/dev/hypera/updatelib/internal/UpdateLib.java b/src/main/java/dev/hypera/updatelib/internal/UpdateLib.java deleted file mode 100644 index 40641a7..0000000 --- a/src/main/java/dev/hypera/updatelib/internal/UpdateLib.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2021 Joshua Sing - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package dev.hypera.updatelib.internal; - -import dev.hypera.updatelib.internal.tasks.UpdateChecker; -import dev.hypera.updatelib.internal.tasks.UpdateTask; - -import java.util.Timer; -import java.util.function.Consumer; - -public class UpdateLib { - - private final static String version = "2.1.1"; - - private final long resourceId; - private final String currentVersion; - private final int connectionTimeout; - private final Consumer consumer; - - private long lastCheck = 0L; - private UpdateResponse lastResponse = null; - - public UpdateLib(long resourceId, String currentVersion, boolean repeatingChecksEnabled, long checkInterval, int connectionTimeout, Consumer consumer) { - this.resourceId = resourceId; - this.currentVersion = currentVersion; - this.connectionTimeout = connectionTimeout; - this.consumer = consumer; - - Thread thread = new Thread(() -> { - try { - checkNow(); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - - thread.setName("UpdateLib-" + thread.getId()); - thread.start(); - - if(repeatingChecksEnabled) { - new Timer().schedule(new UpdateTask(resourceId, currentVersion, connectionTimeout, consumer, this), checkInterval); - } - } - - /** - * Checks for a update now. - * - * @return Response (instance of {@link UpdateResponse}). - * @throws Exception Any errors that occurred while checking. - * @since 2.0.0-SNAPSHOT - */ - public UpdateResponse checkNow() throws Exception { - lastCheck = System.currentTimeMillis(); - lastResponse = new UpdateChecker().check(resourceId, currentVersion, connectionTimeout); - if(null != consumer) - consumer.accept(getLastResponse()); - return lastResponse; - } - - /** - * Get last {@link UpdateResponse} stored - If UpdateLib hasn't checked for updates yet, this will return null. - * - * @return Last {@link UpdateResponse} stored. - * @since 2.0.0-SNAPSHOT - */ - public UpdateResponse getLastResponse() { - return lastResponse; - } - - /** - * Set last response. - Used internally by UpdateLib. - * - * @param lastResponse Last response. - * - * @since 2.0.0-SNAPSHOT - */ - public void setLastResponse(UpdateResponse lastResponse) { - this.lastResponse = lastResponse; - } - - /** - * Get the last time UpdateLib checked for an update. - * - * @return Last time in milliseconds. - * @since 2.0.0-SNAPSHOT - */ - public long getLastCheckTime() { - return lastCheck; - } - - /** - * Set last check time. - Used internally by UpdateLib. - * - * @param lastCheck Last check time in milliseconds. - * - * @since 2.0.0-SNAPSHOT - */ - public void setLastCheck(long lastCheck) { - this.lastCheck = lastCheck; - } - - /** - * Get UpdateLib version. - * - * @return Version. - * @since 2.1.0-SNAPSHOT - */ - public static String getVersion() { - return version; - } - -} diff --git a/src/main/java/dev/hypera/updatelib/internal/tasks/UpdateChecker.java b/src/main/java/dev/hypera/updatelib/internal/tasks/UpdateChecker.java deleted file mode 100644 index 8896abf..0000000 --- a/src/main/java/dev/hypera/updatelib/internal/tasks/UpdateChecker.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2021 Joshua Sing - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package dev.hypera.updatelib.internal.tasks; - -import dev.hypera.updatelib.internal.UpdateResponse; -import org.apache.maven.artifact.versioning.ComparableVersion; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import javax.net.ssl.HttpsURLConnection; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.URL; - -public class UpdateChecker { - - private static final String URL_BASE = "https://api.spigotmc.org/simple/0.1/index.php?action=getResource&id="; - - /** - * Check for an update. - Used internally by UpdateLib. - * - * @param resourceId The ID of the SpigotMC resource. - * @param currentVersion The current version of the plugin. - * @param timeout How long we should wait before timing out the connection or read. - * - * @return Update response. - * @throws Exception Any errors that occur while checking for an update. - * @since 2.0.0-SNAPSHOT - */ - public UpdateResponse check(long resourceId, String currentVersion, int timeout) throws Exception { - URL url = new URL(URL_BASE + resourceId); - - HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection(); - httpsURLConnection.setConnectTimeout(timeout); - httpsURLConnection.setReadTimeout(timeout); - - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream())); - JSONObject json = (JSONObject) JSONValue.parse(readAll(bufferedReader)); - String spigotVersion = (String) json.get("current_version"); - - if(null == spigotVersion) - return null; - return new UpdateResponse(new ComparableVersion(currentVersion).compareTo(new ComparableVersion(spigotVersion)) < 0, currentVersion, spigotVersion); - } - - /** - * Read all from a BufferedReader and return it as a string. - * - * @param bufferedReader BufferedReader to read from. - * - * @return Contents of reader. - * @throws Exception Any errors that occur during execution. - * @since 2.0.0-SNAPSHOT - */ - private String readAll(BufferedReader bufferedReader) throws Exception { - StringBuilder stringBuilder = new StringBuilder(); - String line; - while((line = bufferedReader.readLine()) != null) - stringBuilder.append(line).append("\n"); - return stringBuilder.toString(); - } - -} diff --git a/src/main/java/dev/hypera/updatelib/internal/tasks/UpdateTask.java b/src/main/java/dev/hypera/updatelib/internal/tasks/UpdateTask.java deleted file mode 100644 index a8dd270..0000000 --- a/src/main/java/dev/hypera/updatelib/internal/tasks/UpdateTask.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021 Joshua Sing - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package dev.hypera.updatelib.internal.tasks; - -import dev.hypera.updatelib.internal.UpdateLib; -import dev.hypera.updatelib.internal.UpdateResponse; - -import java.util.TimerTask; -import java.util.function.Consumer; - -public class UpdateTask extends TimerTask { - - private final long resourceId; - private final String currentVersion; - private final int timeout; - private final Consumer consumer; - - private final UpdateLib updateLib; - - public UpdateTask(long resourceId, String currentVersion, int timeout, Consumer consumer, UpdateLib updateLib) { - this.resourceId = resourceId; - this.currentVersion = currentVersion; - this.timeout = timeout; - this.consumer = consumer; - this.updateLib = updateLib; - } - - @Override - public void run() { - Thread thread = new Thread(() -> { - try { - updateLib.setLastResponse(new UpdateChecker().check(resourceId, currentVersion, timeout)); - updateLib.setLastCheck(System.currentTimeMillis()); - - if(null != consumer) consumer.accept(updateLib.getLastResponse()); - } catch (Exception ex) { - ex.printStackTrace(); - } - }); - thread.setName("UpdateLib-" + thread.getId()); - thread.start(); - } - -} diff --git a/src/main/java/dev/hypera/updatelib/objects/UpdateStatus.java b/src/main/java/dev/hypera/updatelib/objects/UpdateStatus.java new file mode 100644 index 0000000..ec90a7f --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/objects/UpdateStatus.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.objects; + +public class UpdateStatus { + + private final String distributedVersion; + private final String currentVersion; + private final Status status; + + /** + * UpdateStatus Constructor - Used internally by UpdateLib. + * + * @param distributedVersion Latest distributed resource version. + * @param currentVersion Current resource version. + * @param status Update status. + * + * @since 3.0.0-SNAPSHOT + */ + protected UpdateStatus(String distributedVersion, String currentVersion, Status status) { + this.distributedVersion = distributedVersion; + this.currentVersion = currentVersion; + this.status = status; + } + + /** + * Get latest distributed version. + * + * @return Distributed version. + * @since 3.0.0-SNAPSHOT + */ + public String getDistributedVersion() { + return distributedVersion; + } + + /** + * Get current resource version. + * + * @return Current version. + * @since 3.0.0-SNAPSHOT + */ + public String getCurrentVersion() { + return currentVersion; + } + + /** + * Get update status. + * + * @return Update status. + * @since 3.0.0-SNAPSHOT + */ + public Status getStatus() { + return status; + } + + /** + * If an update is available. + * @return Update available. + * @since 3.0.0-SNAPSHOT + */ + public boolean isAvailable() { + return status.equals(Status.AVAILABLE) || status.equals(Status.MAJOR_AVAILABLE) || status.equals(Status.MINOR_AVAILABLE); + } + + public enum Status { + MAJOR_AVAILABLE, + MINOR_AVAILABLE, + AVAILABLE, + UNAVAILABLE, + FAILED; + + public static Status fromNumber(VersionScheme scheme, int i) { + if(scheme.equals(VersionScheme.CALENDAR)) return AVAILABLE; + + switch(i) { + case 2: + return MAJOR_AVAILABLE; + case 3: + return MINOR_AVAILABLE; + default: + return AVAILABLE; + } + } + } + +} diff --git a/src/main/java/dev/hypera/updatelib/objects/UpdateStatusBuilder.java b/src/main/java/dev/hypera/updatelib/objects/UpdateStatusBuilder.java new file mode 100644 index 0000000..90d2e0e --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/objects/UpdateStatusBuilder.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.objects; + +import dev.hypera.updatelib.annotations.Builder; +import dev.hypera.updatelib.annotations.Internal; +import dev.hypera.updatelib.exceptions.VersionSchemeException; +import dev.hypera.updatelib.utils.Check; +import dev.hypera.updatelib.utils.VersionUtils; + +import java.util.Optional; + +@Builder +@Internal +public class UpdateStatusBuilder { + + private final String distributedVersion; + private final String currentVersion; + + private VersionScheme scheme; + + /** + * UpdateStatusBuilder Constructor - Used internally by UpdateLib. + * + * @param distributedVersion Latest distributed resource version. + * @param currentVersion Current resource version. + * + * @since 3.0.0-SNAPSHOT + */ + private UpdateStatusBuilder(String distributedVersion, String currentVersion) { + Check.notNull("currentVersion", currentVersion); + this.distributedVersion = distributedVersion; + this.currentVersion = currentVersion; + } + + /** + * Create a new instance of {@link UpdateStatusBuilder} + * + * @param distributedVersion Latest distributed resource version. + * @param currentVersion Current resource version. + * + * @return {@link UpdateStatusBuilder} + * @since 3.0.0-SNAPSHOT + */ + public static UpdateStatusBuilder create(String distributedVersion, String currentVersion) { + return new UpdateStatusBuilder(distributedVersion, currentVersion); + } + + /** + * Set version scheme. + * + * @param scheme {@link VersionScheme} to use. + * + * @return {@link UpdateStatusBuilder} + * @since 3.0.0-SNAPSHOT + */ + public UpdateStatusBuilder setVersionScheme(VersionScheme scheme) { + this.scheme = scheme; + return this; + } + + /** + * Builds a new instance of {@link UpdateStatus} + * + * @return Instance of {@link UpdateStatus} + * @since 3.0.0-SNAPSHOT + */ + public UpdateStatus build() { + if(null == distributedVersion) + return new UpdateStatus(null, currentVersion, UpdateStatus.Status.FAILED); + + if(null == scheme) { + Optional versionScheme = VersionUtils.detectScheme(distributedVersion); + Optional currentVersionScheme = VersionUtils.detectScheme(currentVersion); + + if(!versionScheme.isPresent() || !currentVersionScheme.isPresent()) + throw new VersionSchemeException("Cannot find version scheme for '" + distributedVersion + "'/'" + currentVersion + "'"); + if(!versionScheme.get().equals(currentVersionScheme.get())) + throw new VersionSchemeException("Current and distributed version schemes must match."); + + scheme = versionScheme.get(); + } + + return new UpdateStatus(distributedVersion, currentVersion, VersionUtils.compare(scheme, distributedVersion, currentVersion)); + } + +} diff --git a/src/main/java/dev/hypera/updatelib/objects/VersionScheme.java b/src/main/java/dev/hypera/updatelib/objects/VersionScheme.java new file mode 100644 index 0000000..b112a39 --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/objects/VersionScheme.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.objects; + +import java.util.regex.Pattern; + +public enum VersionScheme { + + BASIC("MAJOR.MINOR", "^(v?)(?0|[1-9]\\d*)\\.(?0|[1-9]\\d*)(?:-(?(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?$"), + SEMANTIC("MAJOR.MINOR.PATCH - https://semver.org/", "^(v?)(?0|[1-9]\\d*)\\.(?0|[1-9]\\d*)\\.(?0|[1-9]\\d*)(?:-(?(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+(?[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"), + CALENDAR("YYYY-MM-DD - https://calver.org", "^(v?)(?\\d{4})-(?\\d{2})-(?\\d{2})$"); + + private final String description; + private final Pattern pattern; + + VersionScheme(String description, String regex) { + this.description = description; + this.pattern = Pattern.compile(regex); + } + + /** + * Get human readable description of the Version Scheme. + * + * @return Description. + * @since 3.0.0-SNAPSHOT + */ + public String getDescription() { + return description; + } + + /** + * Get the regex pattern of the Version Scheme. + * + * @return Regex pattern. + * @since 3.0.0-SNAPSHOT + */ + public Pattern getPattern() { + return pattern; + } + +} diff --git a/src/main/java/dev/hypera/updatelib/utils/Check.java b/src/main/java/dev/hypera/updatelib/utils/Check.java new file mode 100644 index 0000000..b2b8cba --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/utils/Check.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.utils; + +import dev.hypera.updatelib.annotations.Internal; + +@Internal +public class Check { + + /** + * Check if object is null, if so, throw an IllegalArgumentException. + * + * @param name Name of the object. + * @param object Object. + * + * @since 3.0.0-SNAPSHOT + */ + public static void notNull(String name, Object object) { + if(null == object) + fail(name + " cannot be null"); + } + + /** + * Check if objects are null, if so, throw an IllegalArgumentException. + * + * @param names Object names. + * @param objects Objects to check. + * + * @since 3.1.2-SNAPSHOT + */ + public static void notNull(String[] names, Object... objects) { + for(int i = 0; i < objects.length; i++) { + if(null == objects[i]) + fail(names[i] + " cannot be null."); + } + } + + /** + * Throw an IllegalArgumentException. + * + * @param message Message. + * + * @since 3.0.0-SNAPSHOT + */ + private static void fail(String message) { + throw new IllegalArgumentException(message); + } + +} diff --git a/src/main/java/dev/hypera/updatelib/utils/ReaderUtils.java b/src/main/java/dev/hypera/updatelib/utils/ReaderUtils.java new file mode 100644 index 0000000..c81ccfe --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/utils/ReaderUtils.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.utils; + +import dev.hypera.updatelib.annotations.Internal; + +import java.io.BufferedReader; + +@Internal +public class ReaderUtils { + + /** + * Read all from BufferedReader. + * + * @param bufferedReader BufferedReader to read from. + * + * @return Buffer content. + * @throws Exception Any exceptions that occur. + * @since 3.0.0-SNAPSHOT + */ + public static String readAll(BufferedReader bufferedReader) throws Exception { + StringBuilder stringBuilder = new StringBuilder(); + + String line; + while((line = bufferedReader.readLine()) != null) { + stringBuilder.append(line).append("\n"); + } + + return stringBuilder.toString(); + } + +} diff --git a/src/main/java/dev/hypera/updatelib/utils/VersionUtils.java b/src/main/java/dev/hypera/updatelib/utils/VersionUtils.java new file mode 100644 index 0000000..509d08b --- /dev/null +++ b/src/main/java/dev/hypera/updatelib/utils/VersionUtils.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021 Joshua Sing + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package dev.hypera.updatelib.utils; + +import dev.hypera.updatelib.annotations.Internal; +import dev.hypera.updatelib.exceptions.VersionSchemeException; +import dev.hypera.updatelib.objects.UpdateStatus; +import dev.hypera.updatelib.objects.VersionScheme; + +import java.util.Arrays; +import java.util.Optional; +import java.util.regex.Matcher; + +@Internal +public class VersionUtils { + + /** + * Detect {@link VersionScheme} from string. - Used internally by UpdateLib. + * + * @param version Version string. + * + * @return Optional {@link VersionScheme} + * @since 3.0.0-SNAPSHOT + */ + public static Optional detectScheme(String version) { + return Arrays.stream(VersionScheme.values()).filter(scheme -> scheme.getPattern().matcher(version).find()).findFirst(); + } + + /** + * Compare two versions. - Used internally by UpdateLib. + * + * @param versionScheme Version scheme. + * @param newVersion New/Distributed version. + * @param currentVersion Current resource version. + * + * @return {@link UpdateStatus.Status} + * @since 3.0.0-SNAPSHOT + */ + public static UpdateStatus.Status compare(VersionScheme versionScheme, String newVersion, String currentVersion) { + Check.notNull(new String[] { "version scheme", "new version", "current version" }, versionScheme, newVersion, currentVersion); + + if(newVersion.equals(currentVersion)) + return UpdateStatus.Status.UNAVAILABLE; + else if(versionScheme.equals(VersionScheme.CALENDAR)) + return UpdateStatus.Status.AVAILABLE; + + Matcher matcher = versionScheme.getPattern().matcher(newVersion); + Matcher currentMatcher = versionScheme.getPattern().matcher(currentVersion); + + if(!matcher.find() || !currentMatcher.find()) + throw new VersionSchemeException("Version does not matcher version scheme."); + + for(int i = 1; i < Math.max(currentMatcher.groupCount(), matcher.groupCount()); i++) { + if(safeCheck(currentMatcher.group(i), matcher.group(i))) + return UpdateStatus.Status.fromNumber(versionScheme, i); + } + + return UpdateStatus.Status.AVAILABLE; + } + + /** + * Safely check two integers. - Used internally by UpdateLib. + * + * @param currentGroup Group 1 + * @param newGroup Group 2 + * + * @return If newGroup > currentGroup. + * @since 3.0.0-SNAPSHOT + */ + private static boolean safeCheck(String currentGroup, String newGroup) { + if(isEmpty(currentGroup) && isEmpty(newGroup)) + return false; + + if(isEmpty(currentGroup) || isEmpty(newGroup)) + return true; + + int currentInt; + int newInt; + + try { + currentInt = Integer.parseInt(currentGroup); + newInt = Integer.parseInt(newGroup); + } catch (Exception ex) { + return !newGroup.equals(currentGroup); + } + + return newInt > currentInt; + } + + private static boolean isEmpty(String one) { + if(null == one) + return true; + return one.equals(""); + } + +}