0.1.1 | Scheduler shenanigans
This commit is contained in:
@@ -67,4 +67,73 @@ public class main {
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Usage Scheduler (without retries)
|
||||
|
||||
```java
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class main {
|
||||
public static void main(String[] args) {
|
||||
LicenseScheduler scheduler = LicenseScheduler.startScheduler(
|
||||
Duration.ofMinutes(5),
|
||||
apiUrl,
|
||||
plugin,
|
||||
licenseKey,
|
||||
serverId,
|
||||
result -> {
|
||||
if (result instanceof LicenseSuccess success) {
|
||||
if (!success.valid()) {
|
||||
// Handle invalid license
|
||||
}
|
||||
} else if (result instanceof LicenseError error) {
|
||||
// Log errors if you want
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Later: Stop the scheduler
|
||||
scheduler.stop();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Usage Scheduler (with retries)
|
||||
|
||||
```java
|
||||
import java.time.Duration;
|
||||
|
||||
public class main {
|
||||
public static void main(String[] args) {
|
||||
LicenseScheduler scheduler = LicenseScheduler.startSchedulerWithRetries(
|
||||
Duration.ofMinutes(5),
|
||||
apiUrl,
|
||||
plugin,
|
||||
licenseKey,
|
||||
serverId,
|
||||
3, //max retries,
|
||||
Duration.ofSeconds(10), //delay between retries
|
||||
result -> {
|
||||
if(result instanceof LicenseSuccess success) {
|
||||
if (!success.valid()) {
|
||||
// Handle invalid license
|
||||
}
|
||||
} else if (result instanceof LicenseError error) {
|
||||
// Log errors if you want
|
||||
}
|
||||
},
|
||||
result -> {
|
||||
// Called only after all retries failed
|
||||
// Put your "offline" handling here
|
||||
// Disable plugin or something like that
|
||||
}
|
||||
);
|
||||
|
||||
// Later: Stop the scheduler
|
||||
scheduler.stop();
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,49 +0,0 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
group = 'de.winniepat'
|
||||
version = '0.1.0'
|
||||
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {url = "https://repo.papermc.io/repository/maven-public/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.code.gson:gson:2.13.1'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url = uri("https://maven.winniepat.de/repository/maven-releases/")
|
||||
|
||||
credentials {
|
||||
username = project.property("publish.username")
|
||||
password = project.property("publish.password")
|
||||
// the credentials from before are not valid anymore (I am not that dumb)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
-27
@@ -1,41 +1,50 @@
|
||||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* This generated file contains a sample Java library project to get you started.
|
||||
* For more details on building Java & JVM projects, please refer to https://docs.gradle.org/9.5.1/userguide/building_java_projects.html in the Gradle documentation.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
// Apply the java-library plugin for API and implementation separation.
|
||||
id 'java-library'
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Use Maven Central for resolving dependencies.
|
||||
mavenCentral()
|
||||
}
|
||||
group = 'de.winniepat'
|
||||
version = '0.1.1'
|
||||
|
||||
dependencies {
|
||||
// Use JUnit Jupiter for testing.
|
||||
testImplementation libs.junit.jupiter
|
||||
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
|
||||
// This dependency is exported to consumers, that is to say found on their compile classpath.
|
||||
api libs.commons.math3
|
||||
|
||||
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
|
||||
implementation libs.guava
|
||||
}
|
||||
|
||||
// Apply a specific Java toolchain to ease working on different environments.
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {url = "https://repo.papermc.io/repository/maven-public/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT'
|
||||
implementation 'com.google.code.gson:gson:2.13.1'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
// Use JUnit Platform for unit tests.
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url = uri("https://maven.winniepat.de/repository/maven-releases/")
|
||||
|
||||
credentials {
|
||||
username = project.property("publish.username")
|
||||
password = project.property("publish.password")
|
||||
// the credentials from before are not valid anymore (I am not that dumb)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* Licensed under the MIT License
|
||||
*/
|
||||
|
||||
package de.winniepat.licenselib;
|
||||
package licenselib;
|
||||
|
||||
import com.google.gson.*;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package de.winniepat.licenselib;
|
||||
package licenselib;
|
||||
|
||||
/**
|
||||
* Represents an error that occurred during the license check process, such as network issues or invalid responses from the server.
|
||||
@@ -0,0 +1,194 @@
|
||||
package licenselib;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Periodically checks a license and exposes the most recent result.
|
||||
*/
|
||||
public final class LicenseScheduler implements AutoCloseable {
|
||||
|
||||
private final ScheduledExecutorService executor;
|
||||
private final ScheduledFuture<?> task;
|
||||
private final AtomicReference<LicenseClient.LicenseResult> lastResult;
|
||||
|
||||
private LicenseScheduler(
|
||||
ScheduledExecutorService executor,
|
||||
ScheduledFuture<?> task,
|
||||
AtomicReference<LicenseClient.LicenseResult> lastResult
|
||||
) {
|
||||
this.executor = executor;
|
||||
this.task = task;
|
||||
this.lastResult = lastResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a scheduler that checks the license at the given interval.
|
||||
* @param interval Interval between checks
|
||||
* @param apiUrl Server API url
|
||||
* @param plugin Plugin id matching the one on the backend api
|
||||
* @param licenseKey License key issued by the api
|
||||
* @param serverId Server id matching the one on the backend api
|
||||
* @param onResult Optional callback invoked after every check
|
||||
* @return Scheduler handle for stopping and reading the last result
|
||||
*/
|
||||
public static LicenseScheduler startScheduler(
|
||||
Duration interval,
|
||||
String apiUrl,
|
||||
String plugin,
|
||||
String licenseKey,
|
||||
String serverId,
|
||||
Consumer<LicenseClient.LicenseResult> onResult
|
||||
) {
|
||||
Objects.requireNonNull(interval, "interval");
|
||||
if (interval.isZero() || interval.isNegative()) {
|
||||
throw new IllegalArgumentException("interval must be positive");
|
||||
}
|
||||
|
||||
AtomicReference<LicenseClient.LicenseResult> lastResult = new AtomicReference<>();
|
||||
ThreadFactory threadFactory = runnable -> {
|
||||
Thread thread = new Thread(runnable, "license-scheduler");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
};
|
||||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(threadFactory);
|
||||
|
||||
ScheduledFuture<?> task = executor.scheduleAtFixedRate(() -> {
|
||||
LicenseClient.LicenseResult result = LicenseClient.check(apiUrl, plugin, licenseKey, serverId);
|
||||
lastResult.set(result);
|
||||
if (onResult != null) {
|
||||
onResult.accept(result);
|
||||
}
|
||||
}, 0, interval.toMillis(), TimeUnit.MILLISECONDS);
|
||||
|
||||
return new LicenseScheduler(executor, task, lastResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a scheduler that retries on errors before invoking offline handling.
|
||||
* @param interval Interval between checks
|
||||
* @param apiUrl Server API url
|
||||
* @param plugin Plugin id matching the one on the backend api
|
||||
* @param licenseKey License key issued by the api
|
||||
* @param serverId Server id matching the one on the backend api
|
||||
* @param maxRetries Number of retries after a failed check
|
||||
* @param retryDelay Delay between retries
|
||||
* @param onResult Optional callback invoked after every attempt
|
||||
* @param onOffline Invoked only after all retries fail
|
||||
* @return Scheduler handle for stopping and reading the last result
|
||||
*/
|
||||
public static LicenseScheduler startSchedulerWithRetries(
|
||||
Duration interval,
|
||||
String apiUrl,
|
||||
String plugin,
|
||||
String licenseKey,
|
||||
String serverId,
|
||||
int maxRetries,
|
||||
Duration retryDelay,
|
||||
Consumer<LicenseClient.LicenseResult> onResult,
|
||||
Consumer<LicenseClient.LicenseResult> onOffline
|
||||
) {
|
||||
Objects.requireNonNull(interval, "interval");
|
||||
Objects.requireNonNull(retryDelay, "retryDelay");
|
||||
if (interval.isZero() || interval.isNegative()) {
|
||||
throw new IllegalArgumentException("interval must be positive");
|
||||
}
|
||||
if (retryDelay.isNegative()) {
|
||||
throw new IllegalArgumentException("retryDelay must not be negative");
|
||||
}
|
||||
if (maxRetries < 0) {
|
||||
throw new IllegalArgumentException("maxRetries must not be negative");
|
||||
}
|
||||
|
||||
AtomicReference<LicenseClient.LicenseResult> lastResult = new AtomicReference<>();
|
||||
ThreadFactory threadFactory = runnable -> {
|
||||
Thread thread = new Thread(runnable, "license-scheduler");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
};
|
||||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(threadFactory);
|
||||
|
||||
ScheduledFuture<?> task = executor.scheduleAtFixedRate(() -> {
|
||||
LicenseClient.LicenseResult result = attemptWithRetries(
|
||||
apiUrl,
|
||||
plugin,
|
||||
licenseKey,
|
||||
serverId,
|
||||
maxRetries,
|
||||
retryDelay,
|
||||
onResult,
|
||||
onOffline
|
||||
);
|
||||
lastResult.set(result);
|
||||
}, 0, interval.toMillis(), TimeUnit.MILLISECONDS);
|
||||
|
||||
return new LicenseScheduler(executor, task, lastResult);
|
||||
}
|
||||
|
||||
private static LicenseClient.LicenseResult attemptWithRetries(
|
||||
String apiUrl,
|
||||
String plugin,
|
||||
String licenseKey,
|
||||
String serverId,
|
||||
int maxRetries,
|
||||
Duration retryDelay,
|
||||
Consumer<LicenseClient.LicenseResult> onResult,
|
||||
Consumer<LicenseClient.LicenseResult> onOffline
|
||||
) {
|
||||
int attempt = 0;
|
||||
LicenseClient.LicenseResult result;
|
||||
|
||||
while (true) {
|
||||
result = LicenseClient.check(apiUrl, plugin, licenseKey, serverId);
|
||||
if (onResult != null) {
|
||||
onResult.accept(result);
|
||||
}
|
||||
if (!(result instanceof LicenseError)) {
|
||||
return result;
|
||||
}
|
||||
if (attempt >= maxRetries) {
|
||||
if (onOffline != null) {
|
||||
onOffline.accept(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
attempt++;
|
||||
if (!retryDelay.isZero()) {
|
||||
try {
|
||||
Thread.sleep(retryDelay.toMillis());
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most recent license check result, or null if none has completed yet.
|
||||
* @return The last license check result, or null if not available
|
||||
*/
|
||||
public LicenseClient.LicenseResult getLastResult() {
|
||||
return lastResult.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops further checks and shuts down the scheduler.
|
||||
*/
|
||||
public void stop() {
|
||||
task.cancel(false);
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package de.winniepat.licenselib;
|
||||
package licenselib;
|
||||
|
||||
/**
|
||||
* Represents a successful license check result, containing all relevant information about the license status, plugin, customer, and expiration details.
|
||||
@@ -1,10 +0,0 @@
|
||||
/*
|
||||
* This source file was generated by the Gradle 'init' task
|
||||
*/
|
||||
package org.example;
|
||||
|
||||
public class Library {
|
||||
public boolean someLibraryMethod() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
* This source file was generated by the Gradle 'init' task
|
||||
*/
|
||||
package org.example;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class LibraryTest {
|
||||
@Test void someLibraryMethodReturnsTrue() {
|
||||
Library classUnderTest = new Library();
|
||||
assertTrue(classUnderTest.someLibraryMethod(), "someLibraryMethod should return 'true'");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user