ferrybig

BukkitWorker

Nov 14th, 2015
186
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 11.49 KB | None | 0 0
  1. package me.ferry.bukkit.plugins;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.Collections;
  5. import java.util.List;
  6. import java.util.concurrent.Callable;
  7. import java.util.concurrent.ExecutionException;
  8. import java.util.concurrent.FutureTask;
  9. import java.util.concurrent.RunnableFuture;
  10. import java.util.concurrent.TimeUnit;
  11. import java.util.concurrent.TimeoutException;
  12. import org.bukkit.Bukkit;
  13. import org.bukkit.plugin.Plugin;
  14.  
  15. /**
  16.  * An abstract class to perform lengthy Server-interacting tasks in a dedicated
  17.  * thread.
  18.  *
  19.  * <p>
  20.  * When writing a multi-threaded application using bukkit, there are two
  21.  * constraints to keep in mind:
  22.  * <ul>
  23.  * <li> Time-consuming tasks should not be run on the <i>Bukkit Main Server
  24.  * Thread</i>. Otherwise the server becomes unresponsive.
  25.  * </li>
  26.  * <li> Bukkit components should be accessed on the <i>Bukkit Main Server
  27.  * Thread</i> only.
  28.  * </li>
  29.  * </ul>
  30.  * Note: the done methodes may not been called if the plujgin that is running
  31.  * this instance is stopped/disabled
  32.  *
  33.  * @param <T> the result type returned by this {@code BukkitWorker's}
  34.  * {@code doInBackground} and {@code get} methods
  35.  * @param <V> <V> the type used for carrying out intermediate results by this
  36.  * {@code BukkitWorker's} {@code publish} and {@code process} methods
  37.  * @param <P> Plugin class
  38.  * @author Ferrybig
  39.  */
  40. public abstract class BukkitWorker<T, V, P extends Plugin> implements RunnableFuture<T> {
  41.  
  42.     private AccumulativeRunnable<V> doProcess;
  43.     private final AccumulativeRunnable<Runnable> doSubmit;
  44.     /**
  45.      * everything is run inside this FutureTask. Also it is used as a delegatee
  46.      * for the Future API.
  47.      */
  48.     private final FutureTask<T> future;
  49.     /**
  50.      * The plugin that is running this, because its using generics for this
  51.      * field, you dont need to add your own field for the plugin instace
  52.      */
  53.     protected final P plugin;
  54.     /**
  55.      * current state.
  56.      */
  57.     private volatile StateValue state = StateValue.PENDING;
  58.  
  59.     /**
  60.      * Creates a new BukkitWorker object
  61.      *
  62.      * @param plugin The plugin that is the host of this {@code BukkitWorker}
  63.      */
  64.     public BukkitWorker(P plugin) {
  65.         this.future = new FutureTask<T>(new Callable<T>() {
  66.             @Override
  67.             public T call() throws Exception {
  68.                 BukkitWorker.this.setState(StateValue.STARTED);
  69.                 return BukkitWorker.this.doInBackground();
  70.             }
  71.         }) {
  72.             @Override
  73.             protected void done() {
  74.                 BukkitWorker.this.doneTask();
  75.                 BukkitWorker.this.setState(StateValue.DONE);
  76.             }
  77.         };
  78.         this.doSubmit = new DoSubmitAccumulativeRunnable(plugin);
  79.         this.plugin = plugin;
  80.     }
  81.  
  82.     /**
  83.      * {@inheritDoc}
  84.      * <br/>
  85.      * Notice, this methode is called if the plugin is stopping
  86.      */
  87.     @Override
  88.     public final boolean cancel(boolean mayInterruptIfRunning) {
  89.         return future.cancel(mayInterruptIfRunning);
  90.     }
  91.  
  92.     /**
  93.      * Computes a result, or throws an exception if unable to do so.
  94.      *
  95.      * <p>
  96.      * Note that this method is executed only once.
  97.      *
  98.      * <p>
  99.      * Note: this method is executed in a background thread.
  100.      *
  101.      *
  102.      * @return the computed result
  103.      * @throws Exception if unable to compute a result
  104.      *
  105.      */
  106.     protected abstract T doInBackground() throws Exception;
  107.  
  108.     /**
  109.      * Executed on the <i>Bukkit Main Server Thread</i> after the
  110.      * {@code doInBackground} method is finished. The default implementation
  111.      * does nothing. Subclasses may override this method to perform completion
  112.      * actions on the <i>Bukkit Main Server Thread</i>. Note that you can query
  113.      * status inside the implementation of this method to determine the result
  114.      * of this task or whether this task has been cancelled.
  115.      *
  116.      * @see #doInBackground
  117.      * @see #isCancelled()
  118.      * @see #get
  119.      */
  120.     protected void done() {
  121.     }
  122.  
  123.     /**
  124.      * Invokes {@code done} on the Bukkit Main Server Thread.
  125.      */
  126.     private void doneTask() {
  127.         this.doSubmit.add(new Runnable() {
  128.             @Override
  129.             public void run() {
  130.                 BukkitWorker.this.done();
  131.             }
  132.         });
  133.  
  134.     }
  135.  
  136.     /**
  137.      * Schedules this {@code BukkitWorker} for execution on a <i>worker</i>
  138.      * thread.
  139.      *
  140.      * <p>
  141.      * Note {@code BukkitWorker} is only designed to be executed once. Executing
  142.      * a {@code BukkitWorker} more than once will not result in invoking the
  143.      * {@code doInBackground} method twice.
  144.      *
  145.      */
  146.     public final void execute() {
  147.         if (this.state == StateValue.PENDING) {
  148.             Bukkit.getScheduler().runTaskAsynchronously(this.plugin, this.future);
  149.         }
  150.     }
  151.  
  152.     /**
  153.      * {@inheritDoc}
  154.      * <p>
  155.      * Note: calling {@code get} on the <i>Bukkit Main Server Thread</i> blocks
  156.      * <i>all</i> other tasks from being processed until this
  157.      * {@code BukkitWorker} is complete. (This is not recommend to do!)
  158.      */
  159.     @Override
  160.     public final T get() throws InterruptedException, ExecutionException {
  161.         return this.future.get();
  162.     }
  163.  
  164.     /**
  165.      * {@inheritDoc}
  166.      * <p>
  167.      * Please refer to {@link #get} for more details.
  168.      */
  169.     @Override
  170.     public final T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
  171.         return this.future.get(timeout, unit);
  172.     }
  173.  
  174.     /**
  175.      * get the plugin that created this {@code BukkitWorker}
  176.      *
  177.      * @return the plugin
  178.      */
  179.     public final P getPlugin() {
  180.         return this.plugin;
  181.     }
  182.  
  183.     /**
  184.      * Returns the {@code BukkitWorker} current state.
  185.      *
  186.      * @return the current state
  187.      */
  188.     public final StateValue getState() {
  189.         /*
  190.          * DONE is a speacial case
  191.          * to keep getState and isDone is sync
  192.          */
  193.         if (this.isDone()) {
  194.             return StateValue.DONE;
  195.         } else {
  196.             return this.state;
  197.         }
  198.     }
  199.  
  200.     /**
  201.      * {@inheritDoc}
  202.      */
  203.     @Override
  204.     public final boolean isCancelled() {
  205.         return this.future.isCancelled();
  206.     }
  207.  
  208.     /**
  209.      * {@inheritDoc}
  210.      */
  211.     @Override
  212.     public final boolean isDone() {
  213.         return this.future.isDone();
  214.     }
  215.  
  216.     /**
  217.      * Receives data chunks from the {@code publish} method asynchronously on
  218.      * the
  219.      * <i>Bukkit Main Server Thread</i>.
  220.      *
  221.      * <p>
  222.      * Please refer to the {@link #publish} method for more details.
  223.      *
  224.      * @param chunks intermediate results to process
  225.      *
  226.      * @see #publish
  227.      *
  228.      */
  229.     protected void process(List<V> chunks) {
  230.     }
  231.  
  232.     /**
  233.      * Sends data chunks to the {@link #process} method. This method is to be
  234.      * used from inside the {@code doInBackground} method to deliver
  235.      * intermediate results for processing on the <i>Bukkit Main Server
  236.      * Thread</i> inside the {@code process} method.
  237.      *
  238.      * <p>
  239.      * Because the {@code process} method is invoked asynchronously on the
  240.      * <i>Bukkit Main Server Thread</i>
  241.      * multiple invocations to the {@code publish} method might occur before the
  242.      * {@code process} method is executed. For performance purposes all these
  243.      * invocations are coalesced into one invocation with concatenated
  244.      * arguments.
  245.      *
  246.      * <p>
  247.      * For example:
  248.      *
  249.      * <pre>
  250.      * publish(&quot;1&quot;);
  251.      * publish(&quot;2&quot;, &quot;3&quot;);
  252.      * publish(&quot;4&quot;, &quot;5&quot;, &quot;6&quot;);
  253.      * </pre>
  254.      *
  255.      * might result in:
  256.      *
  257.      * <pre>
  258.      * process(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;)
  259.      * </pre>
  260.      *
  261.      *
  262.      * @param chunks intermediate results to process
  263.      *
  264.      * @see #process
  265.      *
  266.      */
  267.     protected final void publish(V... chunks) {
  268.         synchronized (this) {
  269.             if (this.doProcess == null) {
  270.                 this.doProcess = new AccumulativeRunnable<V>() {
  271.                     @Override
  272.                     public void run(List<V> args) {
  273.                         BukkitWorker.this.process(args);
  274.                     }
  275.  
  276.                     @Override
  277.                     protected void submit() {
  278.                         BukkitWorker.this.doSubmit.add(this);
  279.                     }
  280.                 };
  281.             }
  282.         }
  283.         this.doProcess.add(chunks);
  284.     }
  285.  
  286.     /**
  287.      * Sets this {@code Future} to the result of computation unless it has been
  288.      * cancelled.
  289.      */
  290.     @Override
  291.     public final void run() {
  292.         this.future.run();
  293.     }
  294.  
  295.     /**
  296.      * Sets this {@code BukkitWorker} state bound property.
  297.      *
  298.      * @param state the state to set
  299.      */
  300.     private void setState(StateValue state) {
  301.         StateValue old = this.state;
  302.         this.state = state;
  303.     }
  304.  
  305.     private static abstract class AccumulativeRunnable<T extends Object> implements Runnable {
  306.  
  307.         private List<T> arguments = null;
  308.  
  309.         protected abstract void run(List<T> paramList);
  310.  
  311.         @Override
  312.         public final void run() {
  313.             this.run(this.flush());
  314.         }
  315.  
  316.         public final synchronized void add(T... toAdd) {
  317.             boolean mustSubmit = false;
  318.             if (this.arguments == null) {
  319.                 mustSubmit = true;
  320.                 this.arguments = new ArrayList<T>();
  321.             }
  322.             Collections.addAll(this.arguments, toAdd);
  323.             if (mustSubmit) {
  324.                 this.submit();
  325.             }
  326.         }
  327.  
  328.         abstract protected void submit();
  329.  
  330.         private synchronized List<T> flush() {
  331.             List<T> localList = this.arguments;
  332.             this.arguments = null;
  333.             return localList;
  334.         }
  335.     }
  336.  
  337.     /**
  338.      * Values for the {@link #getState() } methode.
  339.      */
  340.     public enum StateValue {
  341.  
  342.         /**
  343.          * Initial {@code BukkitWorker} state.
  344.          */
  345.         PENDING,
  346.         /**
  347.          * {@code BukkitWorker} is {@code STARTED} before invoking
  348.          * {@code doInBackground}.
  349.          */
  350.         STARTED,
  351.         /**
  352.          * {@code BukkitWorker} is {@code DONE} after {@code doInBackground}
  353.          * method is finished.
  354.          */
  355.         DONE
  356.     };
  357.  
  358.     private class DoSubmitAccumulativeRunnable extends AccumulativeRunnable<Runnable> implements Runnable {
  359.  
  360.         /**
  361.          * Time in ticks between 2 invokings of the schedular
  362.          */
  363.         private final static int DELAY = 2;
  364.         /**
  365.          * The plugin, used to schedule tasks
  366.          */
  367.         private final Plugin plugin;
  368.  
  369.         DoSubmitAccumulativeRunnable(Plugin plugin) {
  370.             this.plugin = plugin;
  371.         }
  372.  
  373.         @Override
  374.         protected void run(List<Runnable> args) {
  375.             for (Runnable runnable : args) {
  376.                 runnable.run();
  377.             }
  378.         }
  379.  
  380.         @Override
  381.         protected void submit() {
  382.             if (this.plugin.isEnabled()) {
  383.                 Bukkit.getScheduler().scheduleSyncDelayedTask(this.plugin, this, DELAY);
  384.             } else {
  385.                 // What do do, scheduling an task would throw an IllegalArgumentException, and enablking this plugin for a little time may cause bugs inside the plugin?
  386.                 // Mayby use reflection to access the enabled field of JavaPlugin, but this wont work whit plugins that dont exend that class
  387.             }
  388.         }
  389.     }
  390. }
Advertisement
Add Comment
Please, Sign In to add comment