/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.utils.performance.queued; import com.jcwhatever.nucleus.mixins.IPluginOwned; import com.jcwhatever.nucleus.utils.PreCon; import com.jcwhatever.nucleus.utils.observer.future.FutureAgent; import com.jcwhatever.nucleus.utils.observer.future.IFuture; import com.jcwhatever.nucleus.utils.observer.future.IFuture.FutureStatus; import com.jcwhatever.nucleus.utils.text.TextUtils; import org.bukkit.plugin.Plugin; import javax.annotation.Nullable; /** * Abstract class that worker tasks should extend so that they can be used * by {@link QueueWorker} or in a {@link QueueProject}. * * <p>Note that the implementation should invoke {@link #complete} when the operation * it performs is completed successfully or {@link #fail} if the operation failed.</p> */ public abstract class QueueTask implements IPluginOwned, Runnable { private final Plugin _plugin; private final TaskConcurrency _concurrency; private final FutureAgent _resultAgent = new FutureAgent(); private volatile boolean _isRunning = false; private volatile boolean _isComplete = false; private volatile boolean _isCancelled = false; private volatile boolean _isFailed = false; private QueueProject _parent; /** * Constructor. * * @param plugin The owning plugin. * @param concurrency The preferred concurrency. */ public QueueTask (Plugin plugin, TaskConcurrency concurrency) { PreCon.notNull(plugin); PreCon.notNull(concurrency); _plugin = plugin; _concurrency = concurrency; } @Override public final Plugin getPlugin() { return _plugin; } /** * Get the preferred concurrency of the task. */ public final TaskConcurrency getConcurrency() { return _concurrency; } /** * Get the task result future. */ public final IFuture getResult() { return _resultAgent.getFuture(); } /** * Determine if the task is currently running. */ public final boolean isRunning() { return _isRunning; } /** * Determine if the task has successfully completed. */ public final boolean isComplete() { return _isComplete; } /** * Determine if the task is cancelled. */ public final boolean isCancelled() { return _isCancelled; } /** * Determine if the task is ended due to a failure. */ public final boolean isFailed() { return _isFailed; } /** * Determine if the task has ended for any reason. */ public final boolean isEnded() { return _isComplete || _isCancelled || _isFailed; } /** * Invoke to cancel the task. Does not guarantee the task will end * immediately. * * @param reason Optional message describing the reason the task was cancelled. * @param args Optional message format arguments. */ public final IFuture cancel(@Nullable String reason, Object... args) { PreCon.notNull(args); if (isEnded()) return _resultAgent.getFuture(); _isRunning = false; _isCancelled = true; onCancel(); onEnd(); if (reason != null) reason = TextUtils.format(reason, args).toString(); _resultAgent.sendStatus(FutureStatus.CANCEL, reason); if (_parent != null) _parent.update(this); return _resultAgent.getFuture(); } @Override public final void run() { if (_isRunning) return; _isRunning = _plugin.isEnabled(); if (_isRunning) onRun(); } /** * Implementation should invoke this if the task is completed successfully. */ protected final IFuture complete() { if (isEnded()) return _resultAgent.getFuture(); _isRunning = false; _isComplete = true; onComplete(); onEnd(); _resultAgent.sendStatus(FutureStatus.SUCCESS, "Completed."); if (_parent != null) _parent.update(this); return _resultAgent.getFuture(); } /** * Implementation should invoke this if the task fails. * * @param reason Optional message describing the reason the task failed. * @param args Optional message format arguments. */ protected final IFuture fail(@Nullable String reason, Object... args) { if (isEnded()) return _resultAgent.getFuture(); _isRunning = false; _isFailed = true; onFail(); onEnd(); if (reason != null) reason = TextUtils.format(reason, args).toString(); _resultAgent.sendStatus(FutureStatus.ERROR, reason); if (_parent != null) _parent.update(this); return _resultAgent.getFuture(); } /** * Used by the parent project to set itself as parent. */ void setParentProject(QueueProject project) { if (_parent != null) throw new IllegalStateException("A task cannot have more than one project parent."); _parent = project; } /** * Invoked when the task is run. */ protected abstract void onRun(); /** * Invoked if the task is completed successfully. * * <p>Intended for optional override.</p> */ protected void onComplete() {} /** * Invoked if the task fails. * * <p>Intended for optional override.</p> */ protected void onFail() {} /** * Invoked if the task is cancelled. * * <p>Intended for optional override.</p> */ protected void onCancel() {} /** * Invoked when the task is ended for any reason. * * <p>Intended for optional override.</p> */ protected void onEnd() {} }