package me.ferry.bukkit.plugins; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import java.util.concurrent.RunnableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; /** * An abstract class to perform lengthy Server-interacting tasks in a dedicated * thread. * *

* When writing a multi-threaded application using bukkit, there are two * constraints to keep in mind: *

* Note: the done methodes may not been called if the plujgin that is running * this instance is stopped/disabled * * @param the result type returned by this {@code BukkitWorker's} * {@code doInBackground} and {@code get} methods * @param the type used for carrying out intermediate results by this * {@code BukkitWorker's} {@code publish} and {@code process} methods * @param

Plugin class * @author Ferrybig */ public abstract class BukkitWorker implements RunnableFuture { private AccumulativeRunnable doProcess; private final AccumulativeRunnable doSubmit; /** * everything is run inside this FutureTask. Also it is used as a delegatee * for the Future API. */ private final FutureTask future; /** * The plugin that is running this, because its using generics for this * field, you dont need to add your own field for the plugin instace */ protected final P plugin; /** * current state. */ private volatile StateValue state = StateValue.PENDING; /** * Creates a new BukkitWorker object * * @param plugin The plugin that is the host of this {@code BukkitWorker} */ public BukkitWorker(P plugin) { this.future = new FutureTask(new Callable() { @Override public T call() throws Exception { BukkitWorker.this.setState(StateValue.STARTED); return BukkitWorker.this.doInBackground(); } }) { @Override protected void done() { BukkitWorker.this.doneTask(); BukkitWorker.this.setState(StateValue.DONE); } }; this.doSubmit = new DoSubmitAccumulativeRunnable(plugin); this.plugin = plugin; } /** * {@inheritDoc} *
* Notice, this methode is called if the plugin is stopping */ @Override public final boolean cancel(boolean mayInterruptIfRunning) { return future.cancel(mayInterruptIfRunning); } /** * Computes a result, or throws an exception if unable to do so. * *

* Note that this method is executed only once. * *

* Note: this method is executed in a background thread. * * * @return the computed result * @throws Exception if unable to compute a result * */ protected abstract T doInBackground() throws Exception; /** * Executed on the Bukkit Main Server Thread after the * {@code doInBackground} method is finished. The default implementation * does nothing. Subclasses may override this method to perform completion * actions on the Bukkit Main Server Thread. Note that you can query * status inside the implementation of this method to determine the result * of this task or whether this task has been cancelled. * * @see #doInBackground * @see #isCancelled() * @see #get */ protected void done() { } /** * Invokes {@code done} on the Bukkit Main Server Thread. */ private void doneTask() { this.doSubmit.add(new Runnable() { @Override public void run() { BukkitWorker.this.done(); } }); } /** * Schedules this {@code BukkitWorker} for execution on a worker * thread. * *

* Note {@code BukkitWorker} is only designed to be executed once. Executing * a {@code BukkitWorker} more than once will not result in invoking the * {@code doInBackground} method twice. * */ public final void execute() { if (this.state == StateValue.PENDING) { Bukkit.getScheduler().runTaskAsynchronously(this.plugin, this.future); } } /** * {@inheritDoc} *

* Note: calling {@code get} on the Bukkit Main Server Thread blocks * all other tasks from being processed until this * {@code BukkitWorker} is complete. (This is not recommend to do!) */ @Override public final T get() throws InterruptedException, ExecutionException { return this.future.get(); } /** * {@inheritDoc} *

* Please refer to {@link #get} for more details. */ @Override public final T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return this.future.get(timeout, unit); } /** * get the plugin that created this {@code BukkitWorker} * * @return the plugin */ public final P getPlugin() { return this.plugin; } /** * Returns the {@code BukkitWorker} current state. * * @return the current state */ public final StateValue getState() { /* * DONE is a speacial case * to keep getState and isDone is sync */ if (this.isDone()) { return StateValue.DONE; } else { return this.state; } } /** * {@inheritDoc} */ @Override public final boolean isCancelled() { return this.future.isCancelled(); } /** * {@inheritDoc} */ @Override public final boolean isDone() { return this.future.isDone(); } /** * Receives data chunks from the {@code publish} method asynchronously on * the * Bukkit Main Server Thread. * *

* Please refer to the {@link #publish} method for more details. * * @param chunks intermediate results to process * * @see #publish * */ protected void process(List chunks) { } /** * Sends data chunks to the {@link #process} method. This method is to be * used from inside the {@code doInBackground} method to deliver * intermediate results for processing on the Bukkit Main Server * Thread inside the {@code process} method. * *

* Because the {@code process} method is invoked asynchronously on the * Bukkit Main Server Thread * multiple invocations to the {@code publish} method might occur before the * {@code process} method is executed. For performance purposes all these * invocations are coalesced into one invocation with concatenated * arguments. * *

* For example: * *

 * publish("1");
 * publish("2", "3");
 * publish("4", "5", "6");
 * 
* * might result in: * *
 * process("1", "2", "3", "4", "5", "6")
 * 
* * * @param chunks intermediate results to process * * @see #process * */ protected final void publish(V... chunks) { synchronized (this) { if (this.doProcess == null) { this.doProcess = new AccumulativeRunnable() { @Override public void run(List args) { BukkitWorker.this.process(args); } @Override protected void submit() { BukkitWorker.this.doSubmit.add(this); } }; } } this.doProcess.add(chunks); } /** * Sets this {@code Future} to the result of computation unless it has been * cancelled. */ @Override public final void run() { this.future.run(); } /** * Sets this {@code BukkitWorker} state bound property. * * @param state the state to set */ private void setState(StateValue state) { StateValue old = this.state; this.state = state; } private static abstract class AccumulativeRunnable implements Runnable { private List arguments = null; protected abstract void run(List paramList); @Override public final void run() { this.run(this.flush()); } public final synchronized void add(T... toAdd) { boolean mustSubmit = false; if (this.arguments == null) { mustSubmit = true; this.arguments = new ArrayList(); } Collections.addAll(this.arguments, toAdd); if (mustSubmit) { this.submit(); } } abstract protected void submit(); private synchronized List flush() { List localList = this.arguments; this.arguments = null; return localList; } } /** * Values for the {@link #getState() } methode. */ public enum StateValue { /** * Initial {@code BukkitWorker} state. */ PENDING, /** * {@code BukkitWorker} is {@code STARTED} before invoking * {@code doInBackground}. */ STARTED, /** * {@code BukkitWorker} is {@code DONE} after {@code doInBackground} * method is finished. */ DONE }; private class DoSubmitAccumulativeRunnable extends AccumulativeRunnable implements Runnable { /** * Time in ticks between 2 invokings of the schedular */ private final static int DELAY = 2; /** * The plugin, used to schedule tasks */ private final Plugin plugin; DoSubmitAccumulativeRunnable(Plugin plugin) { this.plugin = plugin; } @Override protected void run(List args) { for (Runnable runnable : args) { runnable.run(); } } @Override protected void submit() { if (this.plugin.isEnabled()) { Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, this, DELAY); } else { // What do do, scheduling an task would throw an IllegalArgumentException, and enablking this plugin for a little time may cause bugs inside the plugin? // Mayby use reflection to access the enabled field of JavaPlugin, but this wont work whit plugins that dont exend that class } } } }

AltStyle によって変換されたページ (->オリジナル) /