SHARE
    TWEET
    ferrybig

    BukkitWorker

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

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