PageRenderTime 41ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/plugins-trunk/CommonControls/common/swingworker/SwingWorker.java

#
Java | 720 lines | 272 code | 53 blank | 395 comment | 21 complexity | 4a5bad859a096ee411c00d89ab5e1fd3 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * $Id: SwingWorker.java,v 1.5 2007/03/01 19:55:54 idk Exp $
  3. *
  4. * Copyright 2005 Sun Microsystems, Inc. All rights
  5. * reserved. Use is subject to license terms.
  6. */
  7. //package org.jdesktop.swingworker;
  8. package common.swingworker;
  9. import java.beans.PropertyChangeListener;
  10. import java.beans.PropertyChangeSupport;
  11. import java.beans.PropertyChangeEvent;
  12. import java.util.List;
  13. import java.util.concurrent.*;
  14. import java.util.concurrent.atomic.AtomicInteger;
  15. import java.util.concurrent.locks.*;
  16. import java.awt.event.*;
  17. import javax.swing.SwingUtilities;
  18. import javax.swing.Timer;
  19. /** A version of javax.swing.SwingWorker for Java before 1.6 that is not needed anymore.
  20. * @author Igor Kushnirskiy
  21. * @version $Revision: 1.5 $ $Date: 2007/03/01 19:55:54 $
  22. *
  23. * @param <T> the result type returned by this {@code SwingWorker's}
  24. * {@code doInBackground} and {@code get} methods
  25. * @param <V> the type used for carrying out intermediate results by this
  26. * {@code SwingWorker's} {@code publish} and {@code process} methods
  27. *
  28. * @deprecated use javax.swing.SwingWorker, as of Java 1.6.
  29. *
  30. */
  31. public abstract class SwingWorker<T, V> implements Future<T>, Runnable {
  32. /**
  33. * number of worker threads.
  34. */
  35. private static final int MAX_WORKER_THREADS = 10;
  36. /**
  37. * current progress.
  38. */
  39. private volatile int progress;
  40. /**
  41. * current state.
  42. */
  43. private volatile StateValue state;
  44. /**
  45. * everything is run inside this FutureTask. Also it is used as
  46. * a delegatee for the Future API.
  47. */
  48. private final FutureTask<T> future;
  49. /**
  50. * all propertyChangeSupport goes through this.
  51. */
  52. private final PropertyChangeSupport propertyChangeSupport;
  53. /**
  54. * handler for {@code process} method.
  55. */
  56. private AccumulativeRunnable<V> doProcess;
  57. /**
  58. * handler for progress property change notifications.
  59. */
  60. private AccumulativeRunnable<Integer> doNotifyProgressChange;
  61. private static final AccumulativeRunnable<Runnable> doSubmit =
  62. new DoSubmitAccumulativeRunnable();
  63. private static ExecutorService executorService = null;
  64. /**
  65. * Values for the {@code state} bound property.
  66. */
  67. public enum StateValue {
  68. /**
  69. * Initial {@code SwingWorker} state.
  70. */
  71. PENDING,
  72. /**
  73. * {@code SwingWorker} is {@code STARTED}
  74. * before invoking {@code doInBackground}.
  75. */
  76. STARTED,
  77. /**
  78. * {@code SwingWorker} is {@code DONE}
  79. * after {@code doInBackground} method
  80. * is finished.
  81. */
  82. DONE
  83. };
  84. /**
  85. * Constructs this {@code SwingWorker}.
  86. */
  87. public SwingWorker() {
  88. Callable<T> callable =
  89. new Callable<T>() {
  90. public T call() throws Exception { // NOPMD
  91. setState(StateValue.STARTED);
  92. return doInBackground();
  93. }
  94. };
  95. future = new FutureTask<T>(callable) {
  96. @Override
  97. protected void done() {
  98. doneEDT();
  99. setState(StateValue.DONE);
  100. }
  101. };
  102. state = StateValue.PENDING;
  103. propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
  104. doProcess = null;
  105. doNotifyProgressChange = null;
  106. }
  107. /**
  108. * Computes a result, or throws an exception if unable to do so.
  109. *
  110. * <p>
  111. * Note that this method is executed only once.
  112. *
  113. * <p>
  114. * Note: this method is executed in a background thread.
  115. *
  116. *
  117. * @return the computed result
  118. * @throws Exception if unable to compute a result
  119. *
  120. */
  121. protected abstract T doInBackground() throws Exception ;
  122. /**
  123. * Sets this {@code Future} to the result of computation unless
  124. * it has been canceled.
  125. */
  126. public final void run() {
  127. future.run();
  128. }
  129. /**
  130. * Sends data chunks to the {@link #process} method. This method is to be
  131. * used from inside the {@code doInBackground} method to deliver
  132. * intermediate results
  133. * for processing on the <i>Event Dispatch Thread</i> inside the
  134. * {@code process} method.
  135. *
  136. * <p>
  137. * Because the {@code process} method is invoked asynchronously on
  138. * the <i>Event Dispatch Thread</i>
  139. * multiple invocations to the {@code publish} method
  140. * might occur before the {@code process} method is executed. For
  141. * performance purposes all these invocations are coalesced into one
  142. * invocation with concatenated arguments.
  143. *
  144. * <p>
  145. * For example:
  146. *
  147. * <pre>
  148. * publish(&quot;1&quot;);
  149. * publish(&quot;2&quot;, &quot;3&quot;);
  150. * publish(&quot;4&quot;, &quot;5&quot;, &quot;6&quot;);
  151. * </pre>
  152. *
  153. * might result in:
  154. *
  155. * <pre>
  156. * process(&quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;)
  157. * </pre>
  158. *
  159. * <p>
  160. * <b>Sample Usage</b>. This code snippet loads some tabular data and
  161. * updates {@code DefaultTableModel} with it. Note that it safe to mutate
  162. * the tableModel from inside the {@code process} method because it is
  163. * invoked on the <i>Event Dispatch Thread</i>.
  164. *
  165. * <pre>
  166. * class TableSwingWorker extends
  167. * SwingWorker&lt;DefaultTableModel, Object[]&gt; {
  168. * private final DefaultTableModel tableModel;
  169. *
  170. * public TableSwingWorker(DefaultTableModel tableModel) {
  171. * this.tableModel = tableModel;
  172. * }
  173. *
  174. * {@code @Override}
  175. * protected DefaultTableModel doInBackground() throws Exception {
  176. * for (Object[] row = loadData();
  177. * ! isCancelled() &amp;&amp; row != null;
  178. * row = loadData()) {
  179. * publish((Object[]) row);
  180. * }
  181. * return tableModel;
  182. * }
  183. *
  184. * {@code @Override}
  185. * protected void process(List&lt;Object[]&gt; chunks) {
  186. * for (Object[] row : chunks) {
  187. * tableModel.addRow(row);
  188. * }
  189. * }
  190. * }
  191. * </pre>
  192. *
  193. * @param chunks intermediate results to process
  194. *
  195. * @see #process
  196. *
  197. */
  198. protected final void publish(V... chunks) {
  199. synchronized (this) {
  200. if (doProcess == null) {
  201. doProcess = new AccumulativeRunnable<V>() {
  202. @Override
  203. public void run(List<V> args) {
  204. process(args);
  205. }
  206. @Override
  207. protected void submit() {
  208. doSubmit.add(this);
  209. }
  210. };
  211. }
  212. }
  213. doProcess.add(chunks);
  214. }
  215. /**
  216. * Receives data chunks from the {@code publish} method asynchronously on the
  217. * <i>Event Dispatch Thread</i>.
  218. *
  219. * <p>
  220. * Please refer to the {@link #publish} method for more details.
  221. *
  222. * @param chunks intermediate results to process
  223. *
  224. * @see #publish
  225. *
  226. */
  227. protected void process(List<V> chunks) {
  228. }
  229. /**
  230. * Executed on the <i>Event Dispatch Thread</i> after the {@code doInBackground}
  231. * method is finished. The default
  232. * implementation does nothing. Subclasses may override this method to
  233. * perform completion actions on the <i>Event Dispatch Thread</i>. Note
  234. * that you can query status inside the implementation of this method to
  235. * determine the result of this task or whether this task has been canceled.
  236. *
  237. * @see #doInBackground
  238. * @see #isCancelled()
  239. * @see #get
  240. */
  241. protected void done() {
  242. }
  243. /**
  244. * Sets the {@code progress} bound property.
  245. * The value should be from 0 to 100.
  246. *
  247. * <p>
  248. * Because {@code PropertyChangeListener}s are notified asynchronously on
  249. * the <i>Event Dispatch Thread</i> multiple invocations to the
  250. * {@code setProgress} method might occur before any
  251. * {@code PropertyChangeListeners} are invoked. For performance purposes
  252. * all these invocations are coalesced into one invocation with the last
  253. * invocation argument only.
  254. *
  255. * <p>
  256. * For example, the following invocations:
  257. *
  258. * <pre>
  259. * setProgress(1);
  260. * setProgress(2);
  261. * setProgress(3);
  262. * </pre>
  263. *
  264. * might result in a single {@code PropertyChangeListener} notification with
  265. * the value {@code 3}.
  266. *
  267. * @param progress the progress value to set
  268. * @throws IllegalArgumentException is value not from 0 to 100
  269. */
  270. protected final void setProgress(int progress) {
  271. if (progress < 0 || progress > 100) {
  272. throw new IllegalArgumentException("the value should be from 0 to 100");
  273. }
  274. if (this.progress == progress) {
  275. return;
  276. }
  277. int oldProgress = this.progress;
  278. this.progress = progress;
  279. if (! getPropertyChangeSupport().hasListeners("progress")) {
  280. return;
  281. }
  282. synchronized (this) {
  283. if (doNotifyProgressChange == null) {
  284. doNotifyProgressChange =
  285. new AccumulativeRunnable<Integer>() {
  286. @Override
  287. public void run(List<Integer> args) {
  288. firePropertyChange("progress",
  289. args.get(0),
  290. args.get(args.size() - 1));
  291. }
  292. @Override
  293. protected void submit() {
  294. doSubmit.add(this);
  295. }
  296. };
  297. }
  298. }
  299. doNotifyProgressChange.add(oldProgress, progress);
  300. }
  301. /**
  302. * Returns the {@code progress} bound property.
  303. *
  304. * @return the progress bound property.
  305. */
  306. public final int getProgress() {
  307. return progress;
  308. }
  309. /**
  310. * Schedules this {@code SwingWorker} for execution on a <i>worker</i>
  311. * thread. There are a number of <i>worker</i> threads available. In the
  312. * event all <i>worker</i> threads are busy handling other
  313. * {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting
  314. * queue.
  315. *
  316. * <p>
  317. * Note:
  318. * {@code SwingWorker} is only designed to be executed once. Executing a
  319. * {@code SwingWorker} more than once will not result in invoking the
  320. * {@code doInBackground} method twice.
  321. */
  322. public final void execute() {
  323. getWorkersExecutorService().execute(this);
  324. }
  325. // Future methods START
  326. /**
  327. * {@inheritDoc}
  328. */
  329. public /*final*/ boolean cancel(boolean mayInterruptIfRunning) {
  330. return future.cancel(mayInterruptIfRunning);
  331. }
  332. /**
  333. * {@inheritDoc}
  334. */
  335. public final boolean isCancelled() {
  336. return future.isCancelled();
  337. }
  338. /**
  339. * {@inheritDoc}
  340. */
  341. public final boolean isDone() {
  342. return future.isDone();
  343. }
  344. /**
  345. * {@inheritDoc}
  346. * <p>
  347. * Note: calling {@code get} on the <i>Event Dispatch Thread</i> blocks
  348. * <i>all</i> events, including repaints, from being processed until this
  349. * {@code SwingWorker} is complete.
  350. *
  351. * <p>
  352. * When you want the {@code SwingWorker} to block on the <i>Event
  353. * Dispatch Thread</i> we recommend that you use a <i>modal dialog</i>.
  354. *
  355. * <p>
  356. * For example:
  357. *
  358. * <pre>
  359. * class SwingWorkerCompletionWaiter extends PropertyChangeListener {
  360. * private JDialog dialog;
  361. *
  362. * public SwingWorkerCompletionWaiter(JDialog dialog) {
  363. * this.dialog = dialog;
  364. * }
  365. *
  366. * public void propertyChange(PropertyChangeEvent event) {
  367. * if (&quot;state&quot;.equals(event.getPropertyName())
  368. * &amp;&amp; SwingWorker.StateValue.DONE == event.getNewValue()) {
  369. * dialog.setVisible(false);
  370. * dialog.dispose();
  371. * }
  372. * }
  373. * }
  374. * JDialog dialog = new JDialog(owner, true);
  375. * swingWorker.addPropertyChangeListener(
  376. * new SwingWorkerCompletionWaiter(dialog));
  377. * swingWorker.execute();
  378. * //the dialog will be visible until the SwingWorker is done
  379. * dialog.setVisible(true);
  380. * </pre>
  381. */
  382. public final T get() throws InterruptedException, ExecutionException {
  383. return future.get();
  384. }
  385. /**
  386. * {@inheritDoc}
  387. * <p>
  388. * Please refer to {@link #get} for more details.
  389. */
  390. public final T get(long timeout, TimeUnit unit) throws InterruptedException,
  391. ExecutionException, TimeoutException {
  392. return future.get(timeout, unit);
  393. }
  394. // Future methods END
  395. // PropertyChangeSupports methods START
  396. /**
  397. * Adds a {@code PropertyChangeListener} to the listener list. The listener
  398. * is registered for all properties. The same listener object may be added
  399. * more than once, and will be called as many times as it is added. If
  400. * {@code listener} is {@code null}, no exception is thrown and no action is taken.
  401. *
  402. * <p>
  403. * Note: This is merely a convenience wrapper. All work is delegated to
  404. * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
  405. *
  406. * @param listener the {@code PropertyChangeListener} to be added
  407. */
  408. public final void addPropertyChangeListener(PropertyChangeListener listener) {
  409. getPropertyChangeSupport().addPropertyChangeListener(listener);
  410. }
  411. /**
  412. * Removes a {@code PropertyChangeListener} from the listener list. This
  413. * removes a {@code PropertyChangeListener} that was registered for all
  414. * properties. If {@code listener} was added more than once to the same
  415. * event source, it will be notified one less time after being removed. If
  416. * {@code listener} is {@code null}, or was never added, no exception is
  417. * thrown and no action is taken.
  418. *
  419. * <p>
  420. * Note: This is merely a convenience wrapper. All work is delegated to
  421. * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
  422. *
  423. * @param listener the {@code PropertyChangeListener} to be removed
  424. */
  425. public final void removePropertyChangeListener(PropertyChangeListener listener) {
  426. getPropertyChangeSupport().removePropertyChangeListener(listener);
  427. }
  428. /**
  429. * Reports a bound property update to any registered listeners. No event is
  430. * fired if {@code old} and {@code new} are equal and non-null.
  431. *
  432. * <p>
  433. * This {@code SwingWorker} will be the source for
  434. * any generated events.
  435. *
  436. * <p>
  437. * When called off the <i>Event Dispatch Thread</i>
  438. * {@code PropertyChangeListeners} are notified asynchronously on
  439. * the <i>Event Dispatch Thread</i>.
  440. * <p>
  441. * Note: This is merely a convenience wrapper. All work is delegated to
  442. * {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.
  443. *
  444. *
  445. * @param propertyName the programmatic name of the property that was
  446. * changed
  447. * @param oldValue the old value of the property
  448. * @param newValue the new value of the property
  449. */
  450. public final void firePropertyChange(String propertyName, Object oldValue,
  451. Object newValue) {
  452. getPropertyChangeSupport().firePropertyChange(propertyName,
  453. oldValue, newValue);
  454. }
  455. /**
  456. * Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}.
  457. * This method is used when flexible access to bound properties support is
  458. * needed.
  459. * <p>
  460. * This {@code SwingWorker} will be the source for
  461. * any generated events.
  462. *
  463. * <p>
  464. * Note: The returned {@code PropertyChangeSupport} notifies any
  465. * {@code PropertyChangeListener}s asynchronously on the <i>Event Dispatch
  466. * Thread</i> in the event that {@code firePropertyChange} or
  467. * {@code fireIndexedPropertyChange} are called off the <i>Event Dispatch
  468. * Thread</i>.
  469. *
  470. * @return {@code PropertyChangeSupport} for this {@code SwingWorker}
  471. */
  472. public final PropertyChangeSupport getPropertyChangeSupport() {
  473. return propertyChangeSupport;
  474. }
  475. // PropertyChangeSupports methods END
  476. /**
  477. * Returns the {@code SwingWorker} state bound property.
  478. *
  479. * @return the current state
  480. */
  481. public final StateValue getState() {
  482. /*
  483. * DONE is a special case
  484. * to keep getState and isDone is sync
  485. */
  486. if (isDone()) {
  487. return StateValue.DONE;
  488. } else {
  489. return state;
  490. }
  491. }
  492. /**
  493. * Sets this {@code SwingWorker} state bound property.
  494. * @param the state state to set
  495. */
  496. private void setState(StateValue state) {
  497. StateValue old = this.state;
  498. this.state = state;
  499. firePropertyChange("state", old, state);
  500. }
  501. /**
  502. * Invokes {@code done} on the EDT.
  503. */
  504. private void doneEDT() {
  505. Runnable doDone =
  506. new Runnable() {
  507. public void run() {
  508. done();
  509. }
  510. };
  511. if (SwingUtilities.isEventDispatchThread()) {
  512. doDone.run();
  513. } else {
  514. doSubmit.add(doDone);
  515. }
  516. }
  517. /**
  518. * returns workersExecutorService.
  519. *
  520. * returns the service stored in the appContext or creates it if
  521. * necessary. If the last one it triggers autoShutdown thread to
  522. * get started.
  523. *
  524. * @return ExecutorService for the {@code SwingWorkers}
  525. * @see #startAutoShutdownThread
  526. */
  527. private static synchronized ExecutorService getWorkersExecutorService() {
  528. if (executorService == null) {
  529. //this creates non-daemon threads.
  530. ThreadFactory threadFactory =
  531. new ThreadFactory() {
  532. final AtomicInteger threadNumber = new AtomicInteger(1);
  533. public Thread newThread(final Runnable r) {
  534. StringBuilder name =
  535. new StringBuilder("SwingWorker-pool-");
  536. name.append(System.identityHashCode(this));
  537. name.append("-thread-");
  538. name.append(threadNumber.getAndIncrement());
  539. Thread t = new Thread(r, name.toString());
  540. if (t.isDaemon())
  541. t.setDaemon(false);
  542. if (t.getPriority() != Thread.NORM_PRIORITY)
  543. t.setPriority(Thread.NORM_PRIORITY);
  544. return t;
  545. }
  546. };
  547. /*
  548. * We want a to have no more than MAX_WORKER_THREADS
  549. * running threads.
  550. *
  551. * We want a worker thread to wait no longer than 1 second
  552. * for new tasks before terminating.
  553. */
  554. executorService = new ThreadPoolExecutor(0, MAX_WORKER_THREADS,
  555. 5L, TimeUnit.SECONDS,
  556. new LinkedBlockingQueue<Runnable>(),
  557. threadFactory) {
  558. private final ReentrantLock pauseLock = new ReentrantLock();
  559. private final Condition unpaused = pauseLock.newCondition();
  560. private boolean isPaused = false;
  561. private final ReentrantLock executeLock = new ReentrantLock();
  562. @Override
  563. public void execute(Runnable command) {
  564. /*
  565. * ThreadPoolExecutor first tries to run task
  566. * in a corePool. If all threads are busy it
  567. * tries to add task to the waiting queue. If it
  568. * fails it run task in maximumPool.
  569. *
  570. * We want corePool to be 0 and
  571. * maximumPool to be MAX_WORKER_THREADS
  572. * We need to change the order of the execution.
  573. * First try corePool then try maximumPool
  574. * pool and only then store to the waiting
  575. * queue. We can not do that because we would
  576. * need access to the private methods.
  577. *
  578. * Instead we enlarge corePool to
  579. * MAX_WORKER_THREADS before the execution and
  580. * shrink it back to 0 after.
  581. * It does pretty much what we need.
  582. *
  583. * While we changing the corePoolSize we need
  584. * to stop running worker threads from accepting new
  585. * tasks.
  586. */
  587. //we need atomicity for the execute method.
  588. executeLock.lock();
  589. try {
  590. pauseLock.lock();
  591. try {
  592. isPaused = true;
  593. } finally {
  594. pauseLock.unlock();
  595. }
  596. setCorePoolSize(MAX_WORKER_THREADS);
  597. super.execute(command);
  598. setCorePoolSize(0);
  599. pauseLock.lock();
  600. try {
  601. isPaused = false;
  602. unpaused.signalAll();
  603. } finally {
  604. pauseLock.unlock();
  605. }
  606. } finally {
  607. executeLock.unlock();
  608. }
  609. }
  610. @Override
  611. protected void afterExecute(Runnable r, Throwable t) {
  612. super.afterExecute(r, t);
  613. pauseLock.lock();
  614. try {
  615. while(isPaused) {
  616. unpaused.await();
  617. }
  618. } catch(InterruptedException ignore) {
  619. } finally {
  620. pauseLock.unlock();
  621. }
  622. }
  623. };
  624. }
  625. return executorService;
  626. }
  627. private static class DoSubmitAccumulativeRunnable
  628. extends AccumulativeRunnable<Runnable> implements ActionListener {
  629. private final static int DELAY = (int) (1000 / 30);
  630. @Override
  631. protected void run(List<Runnable> args) {
  632. for (Runnable runnable : args) {
  633. runnable.run();
  634. }
  635. }
  636. @Override
  637. protected void submit() {
  638. Timer timer = new Timer(DELAY, this);
  639. timer.setRepeats(false);
  640. timer.start();
  641. }
  642. public void actionPerformed(ActionEvent event) {
  643. run();
  644. }
  645. }
  646. private class SwingWorkerPropertyChangeSupport
  647. extends PropertyChangeSupport {
  648. SwingWorkerPropertyChangeSupport(Object source) {
  649. super(source);
  650. }
  651. @Override
  652. public void firePropertyChange(final PropertyChangeEvent evt) {
  653. if (SwingUtilities.isEventDispatchThread()) {
  654. super.firePropertyChange(evt);
  655. } else {
  656. doSubmit.add(
  657. new Runnable() {
  658. public void run() {
  659. SwingWorkerPropertyChangeSupport.this
  660. .firePropertyChange(evt);
  661. }
  662. });
  663. }
  664. }
  665. }
  666. }