From bebe32f5871d98a0e2356635eb0ffe3b6dfc0dbf Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Sat, 21 Nov 2020 00:35:28 +0100 Subject: [PATCH] Replace custom Worker implementation with ForkJoinPool and CompletableFutures. --- .../components/commands/Command.java | 19 +- .../components/commands/CommandWorkers.java | 39 --- .../quartzlib/components/worker/Worker.java | 277 ++++++------------ .../components/worker/WorkerAttributes.java | 59 ---- .../components/worker/WorkerCallback.java | 37 --- .../worker/WorkerCallbackManager.java | 165 ----------- .../worker/WorkerMainThreadExecutor.java | 212 -------------- .../components/worker/WorkerRunnable.java | 41 --- .../fr/zcraft/quartzlib/core/QuartzLib.java | 13 +- src/test/java/fr/zcraft/ztoaster/Toaster.java | 7 +- .../fr/zcraft/ztoaster/ToasterWorker.java | 84 ++---- .../zcraft/ztoaster/commands/AddCommand.java | 9 +- 12 files changed, 158 insertions(+), 804 deletions(-) delete mode 100644 src/main/java/fr/zcraft/quartzlib/components/commands/CommandWorkers.java delete mode 100644 src/main/java/fr/zcraft/quartzlib/components/worker/WorkerAttributes.java delete mode 100644 src/main/java/fr/zcraft/quartzlib/components/worker/WorkerCallback.java delete mode 100644 src/main/java/fr/zcraft/quartzlib/components/worker/WorkerCallbackManager.java delete mode 100644 src/main/java/fr/zcraft/quartzlib/components/worker/WorkerMainThreadExecutor.java delete mode 100644 src/main/java/fr/zcraft/quartzlib/components/worker/WorkerRunnable.java diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/Command.java b/src/main/java/fr/zcraft/quartzlib/components/commands/Command.java index d2c7a823..71549689 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/Command.java +++ b/src/main/java/fr/zcraft/quartzlib/components/commands/Command.java @@ -31,15 +31,20 @@ package fr.zcraft.quartzlib.components.commands; import fr.zcraft.quartzlib.components.commands.CommandException.Reason; +import fr.zcraft.quartzlib.components.i18n.I; import fr.zcraft.quartzlib.components.rawtext.RawText; import fr.zcraft.quartzlib.core.QuartzLib; +import fr.zcraft.quartzlib.tools.PluginLogger; +import fr.zcraft.quartzlib.tools.mojang.UUIDFetcher; import fr.zcraft.quartzlib.tools.text.RawMessage; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import java.io.IOException; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.regex.Pattern; @@ -792,8 +797,18 @@ protected Player getPlayerParameter(int index) throws CommandException * @param callback A consumer that will use the offline player's UUID */ public void offlinePlayerParameter(final String parameter, final Consumer callback){ - CommandWorkers cw=new CommandWorkers(); - cw.OfflineNameFetch(parameter,callback); + CompletableFuture.supplyAsync(() -> { + try { + return UUIDFetcher.fetch(parameter); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + }) + .exceptionally((e) -> { + PluginLogger.warning(I.t("Error while getting player UUID")); + return null; + }) + .thenAccept(callback); } /** * Retrieves a player from its name at the given index, or aborts the diff --git a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandWorkers.java b/src/main/java/fr/zcraft/quartzlib/components/commands/CommandWorkers.java deleted file mode 100644 index 6f3b730a..00000000 --- a/src/main/java/fr/zcraft/quartzlib/components/commands/CommandWorkers.java +++ /dev/null @@ -1,39 +0,0 @@ -package fr.zcraft.quartzlib.components.commands; - -import fr.zcraft.quartzlib.components.i18n.I; -import fr.zcraft.quartzlib.components.worker.Worker; -import fr.zcraft.quartzlib.components.worker.WorkerAttributes; -import fr.zcraft.quartzlib.components.worker.WorkerCallback; -import fr.zcraft.quartzlib.components.worker.WorkerRunnable; -import fr.zcraft.quartzlib.tools.PluginLogger; -import fr.zcraft.quartzlib.tools.mojang.UUIDFetcher; - -import java.util.UUID; -import java.util.function.Consumer; - -@WorkerAttributes(name = "Command's worker", queriesMainThread = true) -public class CommandWorkers extends Worker{ - - public void OfflineNameFetch(final String playerName, final Consumer callback) { - - final WorkerCallback wCallback = new WorkerCallback() { - @Override - public void finished(UUID result) { - callback.accept(result); // Si tout va bien on passe l'UUID au callback - } - @Override - public void errored(Throwable exception) { - PluginLogger.warning(I.t("Error while getting player UUID")); - callback.accept(null); // En cas d'erreur on envoie `null` au callback - } - }; - WorkerRunnable wr = new WorkerRunnable() { - @Override - public UUID run() throws Throwable { - return UUIDFetcher.fetch(playerName); - } - }; - submitQuery(wr, wCallback); - } - -} diff --git a/src/main/java/fr/zcraft/quartzlib/components/worker/Worker.java b/src/main/java/fr/zcraft/quartzlib/components/worker/Worker.java index 47e165ee..74f0ce34 100644 --- a/src/main/java/fr/zcraft/quartzlib/components/worker/Worker.java +++ b/src/main/java/fr/zcraft/quartzlib/components/worker/Worker.java @@ -29,204 +29,109 @@ */ package fr.zcraft.quartzlib.components.worker; -import fr.zcraft.quartzlib.core.QuartzLib; import fr.zcraft.quartzlib.core.QuartzComponent; -import fr.zcraft.quartzlib.tools.PluginLogger; -import fr.zcraft.quartzlib.tools.reflection.Reflection; +import fr.zcraft.quartzlib.core.QuartzLib; -import java.util.ArrayDeque; -import java.util.HashMap; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.*; /** * The base class for workers. * A worker is a thread that can handle multiple tasks, which are executed in a queue. * */ -public abstract class Worker extends QuartzComponent -{ - /*===== Static API =====*/ - static private final HashMap, Worker> runningWorkers = new HashMap(); - static private final HashMap, Worker> runnables = new HashMap(); - - - static protected Future submitToMainThread(Callable callable) - { - return getCallerWorkerFromRunnable()._submitToMainThread(callable); - } - - static protected void submitQuery(WorkerRunnable runnable) - { - getCallerWorker()._submitQuery(runnable); - } - - static protected void submitQuery(WorkerRunnable runnable, WorkerCallback callback) - { - getCallerWorker()._submitQuery(runnable, callback); - } - - static private Worker getCallerWorker() - { - Class caller = Reflection.getCallerClass(Worker.class); - if(caller == null) - throw new IllegalAccessError("Queries must be submitted from a Worker class"); - - return getWorker(caller); - } - - static private Worker getWorker(Class workerClass) - { - Worker worker = runningWorkers.get(workerClass); - if(worker == null) - throw new IllegalStateException("Worker '" + workerClass.getName() + "' has not been correctly initialized"); - - return worker; - } - - static private Worker getCallerWorkerFromRunnable() - { - Class caller = Reflection.getCallerClass(WorkerRunnable.class); - if(caller == null) - throw new IllegalAccessError("Main thread queries must be submitted from a WorkerRunnable"); - - Worker worker = runnables.get(caller); - if(worker == null) - throw new IllegalStateException("Caller runnable does not belong to any worker"); - - return worker; - } - - private final String name; - private final ArrayDeque runQueue = new ArrayDeque<>(); - - private final WorkerCallbackManager callbackManager; - private final WorkerMainThreadExecutor mainThreadExecutor; - private Thread thread; - - public Worker() - { - String tempName = null; - WorkerAttributes attributes = getClass().getAnnotation(WorkerAttributes.class); - - if(attributes != null) - { - tempName = attributes.name(); - this.mainThreadExecutor = attributes.queriesMainThread() ? new WorkerMainThreadExecutor(tempName) : null; - } - else - { - this.mainThreadExecutor = null; - } - - if(tempName == null || tempName.isEmpty()) - tempName = getClass().getSimpleName(); - - this.name = tempName; - this.callbackManager = new WorkerCallbackManager(tempName); - } - +public class Worker extends QuartzComponent implements ExecutorService { + private ForkJoinPool forkJoinPool = null; + private final int threadCount; + + public Worker () { + this(0); + } + + public Worker (int threadCount) { + this.threadCount = threadCount; + QuartzLib.loadComponent(this); + } + @Override - public void onEnable() - { - if(thread != null && thread.isAlive()) - { - PluginLogger.warning("Restarting thread '{0}'.", name); - onDisable(); + protected void onEnable() { + if (this.threadCount <= 0) { + this.forkJoinPool = new ForkJoinPool(); + } else { + this.forkJoinPool = new ForkJoinPool(threadCount); } - callbackManager.init(); - if(mainThreadExecutor != null) mainThreadExecutor.init(); - runningWorkers.put(getClass(), this); - thread = createThread(); - thread.start(); } - + @Override - public void onDisable() - { - thread.interrupt(); - callbackManager.exit(); - if(mainThreadExecutor != null) mainThreadExecutor.exit(); - thread = null; - runningWorkers.remove(getClass()); - } - - private void run() - { - WorkerRunnable currentRunnable; - - while(!Thread.interrupted()) - { - synchronized(runQueue) - { - try - { - while(runQueue.isEmpty()) runQueue.wait(); - } - catch(InterruptedException ex) - { - break; - } - currentRunnable = runQueue.pop(); - } - - try - { - callbackManager.callback(currentRunnable, currentRunnable.run()); - } - catch(Throwable ex) - { - callbackManager.callback(currentRunnable, null, ex); - } - runnables.remove(currentRunnable.getClass()); - } + protected void onDisable() { + this.shutdownNow(); } - - private void _submitQuery(WorkerRunnable runnable) - { - attachRunnable(runnable); - synchronized(runQueue) - { - runQueue.add(runnable); - runQueue.notify(); - } + + /* All of the overrides of the world */ + + @Override + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { + return forkJoinPool.invokeAny(tasks); + } + + @Override + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return forkJoinPool.invokeAny(tasks, timeout, unit); + } + + @Override + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { + return forkJoinPool.invokeAll(tasks, timeout, unit); + } + + @Override + public void execute(Runnable task) { + forkJoinPool.execute(task); + } + + @Override + public ForkJoinTask submit(Callable task) { + return forkJoinPool.submit(task); + } + + @Override + public ForkJoinTask submit(Runnable task, T result) { + return forkJoinPool.submit(task, result); + } + + @Override + public ForkJoinTask submit(Runnable task) { + return forkJoinPool.submit(task); + } + + @Override + public List> invokeAll(Collection> tasks) { + return forkJoinPool.invokeAll(tasks); + } + + @Override + public void shutdown() { + forkJoinPool.shutdown(); + } + + @Override + public List shutdownNow() { + return forkJoinPool.shutdownNow(); + } + + @Override + public boolean isTerminated() { + return forkJoinPool.isTerminated(); + } + + @Override + public boolean isShutdown() { + return forkJoinPool.isShutdown(); + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + return forkJoinPool.awaitTermination(timeout, unit); } - - private void _submitQuery(WorkerRunnable runnable, WorkerCallback callback) - { - callbackManager.setupCallback(runnable, callback); - _submitQuery(runnable); - } - - private Future _submitToMainThread(Callable callable) - { - if(mainThreadExecutor != null) return mainThreadExecutor.submit(callable); - return null; - } - - private Thread createThread() - { - return new Thread(getName()) - { - @Override - public void run() - { - Worker.this.run(); - } - }; - } - - private void attachRunnable(WorkerRunnable runnable) - { - if(runnable.getWorker() != null && runnable.getWorker() != this) - throw new IllegalArgumentException("This runnable is already attached to another worker"); - runnable.setWorker(this); - runnables.put(runnable.getClass(), this); - } - - public String getName() - { - return QuartzLib.getPlugin().getName() + "-" + name; - } - } diff --git a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerAttributes.java b/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerAttributes.java deleted file mode 100644 index 204433b2..00000000 --- a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerAttributes.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright or © or Copr. QuartzLib contributors (2015 - 2020) - * - * This software is governed by the CeCILL-B license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL-B - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL-B license and that you accept its terms. - */ -package fr.zcraft.quartzlib.components.worker; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - - -/** - * Defines various data about a given worker class. - */ - -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE}) -public @interface WorkerAttributes -{ - /** - * Defines the name of the Worker. - * The named is used in various places, such as thread name or logging. - * @return The name of the worker. - */ - String name() default ""; - - /** - * Defines if the Worker needs to query the main thread or not. - * See the {@link Worker} class documentation for more information. - * @return If the Worker queries the main thread; - */ - boolean queriesMainThread() default false; -} diff --git a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerCallback.java b/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerCallback.java deleted file mode 100644 index 8abb23b0..00000000 --- a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerCallback.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright or © or Copr. QuartzLib contributors (2015 - 2020) - * - * This software is governed by the CeCILL-B license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL-B - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL-B license and that you accept its terms. - */ - -package fr.zcraft.quartzlib.components.worker; - -public interface WorkerCallback -{ - public void finished(T result); - public void errored(Throwable exception); -} diff --git a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerCallbackManager.java b/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerCallbackManager.java deleted file mode 100644 index aa4d42a9..00000000 --- a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerCallbackManager.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright or © or Copr. QuartzLib contributors (2015 - 2020) - * - * This software is governed by the CeCILL-B license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL-B - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL-B license and that you accept its terms. - */ - -package fr.zcraft.quartzlib.components.worker; - -import fr.zcraft.quartzlib.core.QuartzLib; -import org.bukkit.Bukkit; -import org.bukkit.scheduler.BukkitTask; - -import java.util.ArrayDeque; -import java.util.HashMap; - -class WorkerCallbackManager implements Runnable -{ - static private final int WATCH_LOOP_DELAY = 5; - - private final HashMap callbacks; - private final ArrayDeque callbackQueue; - - private final String name; - - private BukkitTask selfTask; - - public WorkerCallbackManager(String name) - { - callbacks = new HashMap<>(); - callbackQueue = new ArrayDeque<>(); - this.name = name; - } - - public void init() - { - selfTask = Bukkit.getScheduler().runTaskTimer(QuartzLib.getPlugin(), this, 0, WATCH_LOOP_DELAY); - } - - public void setupCallback(WorkerRunnable runnable, WorkerCallback callback) - { - synchronized(callbacks) - { - callbacks.put(runnable, new WorkerRunnableInfo(callback)); - } - } - - public void callback(WorkerRunnable runnable, T result) - { - callback(runnable, result, null); - } - - public void callback(WorkerRunnable runnable, T result, Throwable exception) - { - WorkerRunnableInfo runnableInfo; - synchronized(callbacks) - { - runnableInfo = callbacks.get(runnable); - } - if(runnableInfo == null) return; - runnableInfo.setRunnableException(exception); - runnableInfo.setResult(result); - - enqueueCallback(runnableInfo); - } - - public void exit() - { - if(selfTask != null) selfTask.cancel(); - } - - private void enqueueCallback(WorkerRunnableInfo runnableInfo) - { - synchronized(callbackQueue) - { - callbackQueue.add(runnableInfo); - } - } - - @Override - public void run() - { - WorkerRunnableInfo currentRunnableInfo; - synchronized(callbackQueue) - { - if(callbackQueue.isEmpty()) return; - currentRunnableInfo = callbackQueue.pop(); - } - - currentRunnableInfo.runCallback(); - } - - private class WorkerRunnableInfo - { - private final WorkerCallback callback; - private T result; - private Throwable runnableException; - - public WorkerRunnableInfo(WorkerCallback callback) - { - this.callback = callback; - this.runnableException = null; - } - - public WorkerCallback getCallback() - { - return callback; - } - - public void runCallback() - { - if(runnableCrashed()) - { - callback.errored(runnableException); - } - else - { - callback.finished(result); - } - } - - public void setResult(T result) - { - this.result = result; - } - - public Throwable getRunnableException() - { - return runnableException; - } - - public void setRunnableException(Throwable runnableException) - { - this.runnableException = runnableException; - } - - public boolean runnableCrashed() - { - return this.runnableException != null; - } - } -} diff --git a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerMainThreadExecutor.java b/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerMainThreadExecutor.java deleted file mode 100644 index cd498ba1..00000000 --- a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerMainThreadExecutor.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright or © or Copr. QuartzLib contributors (2015 - 2020) - * - * This software is governed by the CeCILL-B license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL-B - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL-B license and that you accept its terms. - */ - -package fr.zcraft.quartzlib.components.worker; - -import fr.zcraft.quartzlib.core.QuartzLib; -import org.bukkit.Bukkit; -import org.bukkit.scheduler.BukkitTask; - -import java.util.ArrayDeque; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -class WorkerMainThreadExecutor implements Runnable -{ - static private final int WATCH_LOOP_DELAY = 1; - - private final String name; - private final ArrayDeque mainThreadQueue = new ArrayDeque<>(); - private BukkitTask mainThreadTask; - - public WorkerMainThreadExecutor(String name) - { - this.name = name; - } - - public void init() - { - mainThreadTask = Bukkit.getScheduler().runTaskTimer(QuartzLib.getPlugin(), this, 0, WATCH_LOOP_DELAY); - } - - public void exit() - { - mainThreadTask.cancel(); - mainThreadTask = null; - } - - public Future submit(Callable callable) - { - WorkerFuture future = new WorkerFuture(callable); - synchronized(mainThreadQueue) - { - mainThreadQueue.add(future); - } - return future; - } - - @Override - public void run() - { - WorkerFuture currentFuture; - synchronized(mainThreadQueue) - { - if(mainThreadQueue.isEmpty()) return; - currentFuture = mainThreadQueue.pop(); - } - - currentFuture.runCallable(); - } - - private class WorkerFuture implements Future - { - private final Callable callable; - private boolean isCancelled; - private boolean isDone; - private Exception executionException; - private T value; - - public WorkerFuture(Callable callable) - { - this.callable = callable; - } - - public void runCallable() - { - try - { - value = callable.call(); - } - catch(Exception ex) - { - executionException = ex; - } - finally - { - isDone = true; - synchronized(this){this.notifyAll();} - } - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) - { - if(this.isCancelled || this.isDone) return false; - this.isCancelled = true; - this.isDone = true; - return true; - } - - @Override - public boolean isCancelled() - { - return this.isCancelled; - } - - @Override - public boolean isDone() - { - return this.isDone; - } - - @Override - public T get() throws InterruptedException, ExecutionException - { - waitForCompletion(); - if(executionException != null) throw new ExecutionException(executionException); - return value; - } - - @Override - public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException - { - waitForCompletion(timeout, unit); - if(executionException != null) throw new ExecutionException(executionException); - return value; - } - - private void waitForCompletion(long timeout) throws InterruptedException, TimeoutException - { - synchronized(this) - { - long remainingTime; - long timeoutTime = System.currentTimeMillis() + timeout; - while(!isDone) - { - remainingTime = timeoutTime - System.currentTimeMillis(); - if(remainingTime <= 0) throw new TimeoutException(); - this.wait(remainingTime); - } - } - } - - private void waitForCompletion() throws InterruptedException - { - synchronized(this) - { - while(!isDone) this.wait(); - } - } - - private void waitForCompletion(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException - { - long millis = 0; - switch(unit) - { - case NANOSECONDS: - millis = timeout / 10^6; - break; - case MICROSECONDS: - millis = timeout / 10^3; - break; - case MILLISECONDS: - millis = timeout; - break; - case SECONDS: - millis = timeout * 10^3; - break; - case MINUTES: - millis = timeout * 10^3 * 60; - break; - case HOURS: - millis = timeout * 10^3 * 3600; - break; - case DAYS: - millis = timeout * 10^3 * 3600 * 24; - } - waitForCompletion(millis); - } - - - } -} diff --git a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerRunnable.java b/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerRunnable.java deleted file mode 100644 index 4fdaa2a1..00000000 --- a/src/main/java/fr/zcraft/quartzlib/components/worker/WorkerRunnable.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright or © or Copr. QuartzLib contributors (2015 - 2020) - * - * This software is governed by the CeCILL-B license under French law and - * abiding by the rules of distribution of free software. You can use, - * modify and/ or redistribute the software under the terms of the CeCILL-B - * license as circulated by CEA, CNRS and INRIA at the following URL - * "http://www.cecill.info". - * - * As a counterpart to the access to the source code and rights to copy, - * modify and redistribute granted by the license, users are provided only - * with a limited warranty and the software's author, the holder of the - * economic rights, and the successive licensors have only limited - * liability. - * - * In this respect, the user's attention is drawn to the risks associated - * with loading, using, modifying and/or developing or reproducing the - * software by the user in light of its specific status of free software, - * that may mean that it is complicated to manipulate, and that also - * therefore means that it is reserved for developers and experienced - * professionals having in-depth computer knowledge. Users are therefore - * encouraged to load and test the software's suitability as regards their - * requirements in conditions enabling the security of their systems and/or - * data to be ensured and, more generally, to use and operate it in the - * same conditions as regards security. - * - * The fact that you are presently reading this means that you have had - * knowledge of the CeCILL-B license and that you accept its terms. - */ - -package fr.zcraft.quartzlib.components.worker; - -public abstract class WorkerRunnable -{ - private Worker worker; - - abstract public T run() throws Throwable; - - Worker getWorker() {return worker;} - void setWorker(Worker worker) {this.worker = worker;} -} diff --git a/src/main/java/fr/zcraft/quartzlib/core/QuartzLib.java b/src/main/java/fr/zcraft/quartzlib/core/QuartzLib.java index 6afd16e5..81ad9502 100644 --- a/src/main/java/fr/zcraft/quartzlib/core/QuartzLib.java +++ b/src/main/java/fr/zcraft/quartzlib/core/QuartzLib.java @@ -43,6 +43,7 @@ import java.util.ArrayList; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.function.Supplier; public abstract class QuartzLib { @@ -50,7 +51,7 @@ public abstract class QuartzLib static private final ArrayList> componentsToLoad = new ArrayList<>(); static private Set loadedComponents; - static private ZLibListener listener; + static private QuartzLibListener listener; /** * Initializes QuartzLib. @@ -81,14 +82,14 @@ static public void init(JavaPlugin plugin) * @param component The component to load. * @throws IllegalStateException if QuartzLib was not initialized. */ - static T loadComponent(T component) throws IllegalStateException + static public T loadComponent(T component) throws IllegalStateException { checkInitialized(); //Make sure any loaded component will be correctly unloaded. if(listener == null) { - QuartzLib.listener = registerEvents(new ZLibListener()); + QuartzLib.listener = registerEvents(new QuartzLibListener()); } if(loadedComponents.add(component)) @@ -170,7 +171,7 @@ static public JavaPlugin getPlugin() throws IllegalStateException * This returns a copy of the components list, * * @return the loaded components. - * @throws IllegalStateException + * @throws IllegalStateException if QuartzLib was not initialized. */ static public Set getLoadedComponents() throws IllegalStateException { @@ -220,10 +221,10 @@ static public void unregisterEvents(Listener listener) static private void checkInitialized() throws IllegalStateException { if(plugin == null) - throw new IllegalStateException("Assertion failed: QuartzLib is not correctly inizialized. Make sure QuartzLib.init() or QuartzLib.onLoad() is correctly called."); + throw new IllegalStateException("Assertion failed: QuartzLib is not correctly initialized. Make sure QuartzLib.init() or QuartzPlugin.onLoad() is correctly called."); } - static private class ZLibListener implements Listener + static private class QuartzLibListener implements Listener { @EventHandler public void onPluginDisable(PluginDisableEvent event) diff --git a/src/test/java/fr/zcraft/ztoaster/Toaster.java b/src/test/java/fr/zcraft/ztoaster/Toaster.java index b8648773..30d8140b 100644 --- a/src/test/java/fr/zcraft/ztoaster/Toaster.java +++ b/src/test/java/fr/zcraft/ztoaster/Toaster.java @@ -35,6 +35,7 @@ import fr.zcraft.quartzlib.components.i18n.I18n; import fr.zcraft.quartzlib.components.scoreboard.Sidebar; import fr.zcraft.quartzlib.components.scoreboard.SidebarScoreboard; +import fr.zcraft.quartzlib.components.worker.Worker; import fr.zcraft.quartzlib.core.QuartzPlugin; import fr.zcraft.quartzlib.tools.PluginLogger; import fr.zcraft.ztoaster.commands.AddCommand; @@ -70,6 +71,8 @@ public class Toaster extends QuartzPlugin implements Listener */ private Sidebar toasterSidebar; + public Worker toasterWorker; + protected Toaster(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) { super(loader, description, dataFolder, file); @@ -82,8 +85,10 @@ public void onEnable() toasts = new ArrayList<>(); toastCounter = 0; + + toasterWorker = new Worker(1); // Our toaster is single-slot! - loadComponents(Gui.class, Commands.class, ToasterWorker.class, SidebarScoreboard.class, I18n.class); + loadComponents(Gui.class, Commands.class, SidebarScoreboard.class, I18n.class); Commands.register("toaster", AddCommand.class, OpenCommand.class, ListCommand.class); diff --git a/src/test/java/fr/zcraft/ztoaster/ToasterWorker.java b/src/test/java/fr/zcraft/ztoaster/ToasterWorker.java index a8ebaaa4..10ff92c2 100644 --- a/src/test/java/fr/zcraft/ztoaster/ToasterWorker.java +++ b/src/test/java/fr/zcraft/ztoaster/ToasterWorker.java @@ -28,72 +28,50 @@ * knowledge of the CeCILL-B license and that you accept its terms. */ - package fr.zcraft.ztoaster; import fr.zcraft.quartzlib.components.gui.Gui; import fr.zcraft.quartzlib.components.i18n.I; -import fr.zcraft.quartzlib.components.worker.Worker; -import fr.zcraft.quartzlib.components.worker.WorkerCallback; -import fr.zcraft.quartzlib.components.worker.WorkerRunnable; +import fr.zcraft.quartzlib.core.QuartzLib; import fr.zcraft.quartzlib.tools.PluginLogger; import org.bukkit.entity.Player; -public class ToasterWorker extends Worker +import java.util.concurrent.CompletableFuture; + +public class ToasterWorker { /** * Optimal cooking time for making carefully baked toasts. */ static public int TOAST_COOKING_TIME = 4269; - - static public Toast addToast(final Player cook) - { - return ToasterWorker.addToast(new WorkerCallback() - { - @Override - public void finished(Integer toastId) - { - I.sendT(cook, "DING! Toast {0} is ready !", toastId); - Gui.update(ToastExplorer.class); - } - @Override - public void errored(Throwable exception) - { - PluginLogger.error("Error while toasting", exception); - I.sendT(cook, "{ce}Oh no! A toasted exception !"); - I.sendT(cook, "{ce}See toaster logs for details."); - } - }); - } - - /** - * Creates a new toast, and adds it to the toaster queue. - * @param callback The callback to call when a toast is cooked. - * @return the newly created toast. - */ - static public Toast addToast(WorkerCallback callback) + static public CompletableFuture addToast(final Toast toast, final Player cook) { - final Toast newToast = Toaster.newToast(); - final int toastId = newToast.getToastId(); - - submitQuery(new WorkerRunnable() - { - @Override - public Object run() throws Throwable - { - PluginLogger.info("Cooking toast #{0} ...", toastId); - - Thread.sleep(TOAST_COOKING_TIME); - - PluginLogger.info("Toast #{0} cooked !", toastId); - - newToast.setStatus(Toast.CookingStatus.COOKED); - - return toastId; - } - }, callback); - - return newToast; + return CompletableFuture.runAsync(() -> cookToast(toast), ((Toaster)QuartzLib.getPlugin()).toasterWorker) + .thenRun(() -> { + I.sendT(cook, "DING! Toast {0} is ready !", toast.getToastId()); + Gui.update(ToastExplorer.class); + }) + .exceptionally((exception) -> { + PluginLogger.error("Error while toasting", exception); + I.sendT(cook, "{ce}Oh no! A toasted exception !"); + I.sendT(cook, "{ce}See toaster logs for details."); + return null; + }); + } + + static private void cookToast(Toast toast) { + int toastId = toast.getToastId(); + PluginLogger.info("Cooking toast #{0} ...", toastId); + + try { + Thread.sleep(TOAST_COOKING_TIME); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + PluginLogger.info("Toast #{0} cooked !", toastId); + + toast.setStatus(Toast.CookingStatus.COOKED); } } diff --git a/src/test/java/fr/zcraft/ztoaster/commands/AddCommand.java b/src/test/java/fr/zcraft/ztoaster/commands/AddCommand.java index bd435a27..44ef4ac7 100644 --- a/src/test/java/fr/zcraft/ztoaster/commands/AddCommand.java +++ b/src/test/java/fr/zcraft/ztoaster/commands/AddCommand.java @@ -35,6 +35,7 @@ import fr.zcraft.quartzlib.components.commands.CommandInfo; import fr.zcraft.quartzlib.components.i18n.I; import fr.zcraft.ztoaster.Toast; +import fr.zcraft.ztoaster.Toaster; import fr.zcraft.ztoaster.ToasterWorker; import org.bukkit.entity.Player; @@ -49,15 +50,17 @@ protected void run() throws CommandException if(args.length == 0) { - Toast toast = ToasterWorker.addToast(cook); - cook.sendMessage(I.t("Toast {0} added.", toast.getToastId())); + Toast newToast = Toaster.newToast(); + ToasterWorker.addToast(newToast, cook); + cook.sendMessage(I.t("Toast {0} added.", newToast.getToastId())); } else { int toastCount = getIntegerParameter(0); for(int i = toastCount; i --> 0;) { - ToasterWorker.addToast(cook); + Toast newToast = Toaster.newToast(); + ToasterWorker.addToast(newToast, cook); } cook.sendMessage(I.tn("One toast added.", "{0} toasts added.", toastCount, toastCount));