1818import java .util .ArrayList ;
1919import java .util .List ;
2020import java .util .concurrent .Executor ;
21+ import java .util .concurrent .ExecutorService ;
2122import java .util .concurrent .Executors ;
23+ import java .util .concurrent .Future ;
2224import java .util .concurrent .ScheduledExecutorService ;
2325import java .util .concurrent .TimeUnit ;
2426
@@ -40,18 +42,26 @@ public class BackgroundExecutor {
4042 * executor does not support scheduling (if
4143 * {@link #setExecutor(Executor)} has been called with such an
4244 * executor)
45+ * @return Future associated to the running task
4346 */
44- private static void directExecute (Runnable runnable , int delay ) {
47+ private static Future <?> directExecute (Runnable runnable , int delay ) {
48+ Future <?> future = null ;
4549 if (delay > 0 ) {
4650 /* no serial, but a delay: schedule the task */
4751 if (!(executor instanceof ScheduledExecutorService )) {
4852 throw new IllegalArgumentException ("The executor set does not support scheduling" );
4953 }
50- ((ScheduledExecutorService ) executor ).schedule (runnable , delay , TimeUnit .MILLISECONDS );
54+ future = ((ScheduledExecutorService ) executor ).schedule (runnable , delay , TimeUnit .MILLISECONDS );
5155 } else {
5256 /* no serial, no delay: execute now */
53- executor .execute (runnable );
57+ if (executor instanceof ExecutorService ) {
58+ future = ((ExecutorService ) executor ).submit (runnable );
59+ } else {
60+ /* non-cancellable task */
61+ executor .execute (runnable );
62+ }
5463 }
64+ return future ;
5565 }
5666
5767 /**
@@ -68,12 +78,17 @@ private static void directExecute(Runnable runnable, int delay) {
6878 * executor)
6979 */
7080 public static synchronized void execute (Task task ) {
81+ Future <?> future = null ;
7182 if (task .serial == null || !hasSerialRunning (task .serial )) {
7283 task .executionAsked = true ;
73- directExecute (task , task .delay );
84+ future = directExecute (task , task .delay );
85+ if (task .id != null && future == null ) {
86+ throw new IllegalArgumentException ("The executor set does not support task cancellation" );
87+ }
7488 }
75- if (task .serial != null ) {
89+ if (task .id != null || task . serial != null ) {
7690 /* keep task */
91+ task .future = future ;
7792 tasks .add (task );
7893 }
7994 }
@@ -83,6 +98,8 @@ public static synchronized void execute(Task task) {
8398 *
8499 * @param runnable
85100 * the task to execute
101+ * @param id
102+ * identifier used for task cancellation
86103 * @param delay
87104 * the time from now to delay execution, in milliseconds
88105 * @param serial
@@ -94,8 +111,8 @@ public static synchronized void execute(Task task) {
94111 * {@link #setExecutor(Executor)} has been called with such an
95112 * executor)
96113 */
97- public static void execute (final Runnable runnable , int delay , String serial ) {
98- execute (new Task (delay , serial ) {
114+ public static void execute (final Runnable runnable , String id , int delay , String serial ) {
115+ execute (new Task (id , delay , serial ) {
99116 @ Override
100117 public void execute () {
101118 runnable .run ();
@@ -134,24 +151,28 @@ public static void execute(Runnable runnable) {
134151 * Execute a task after all tasks added with the same non-null
135152 * <code>serial</code> (if any) have completed execution.
136153 *
137- * Equivalent to {@link #execute(Runnable, int, String) execute(runnable, 0,
138- * serial)}.
154+ * Equivalent to {@link #execute(Runnable, String, int, String)
155+ * execute(runnable, id, 0, serial)}.
139156 *
140157 * @param runnable
141158 * the task to execute
159+ * @param id
160+ * identifier used for task cancellation
142161 * @param serial
143162 * the serial queue to use (<code>null</code> or <code>""</code>
144163 * for no serial execution)
145164 */
146- public static void execute (Runnable runnable , String serial ) {
147- execute (runnable , 0 , serial );
165+ public static void execute (Runnable runnable , String id , String serial ) {
166+ execute (runnable , id , 0 , serial );
148167 }
149168
150169 /**
151170 * Change the executor.
152171 *
153172 * Note that if the given executor is not a {@link ScheduledExecutorService}
154- * then executing a task after a delay will not be supported anymore.
173+ * then executing a task after a delay will not be supported anymore. If it
174+ * is not even a {@link ExecutorService} then tasks will not be cancellable
175+ * anymore.
155176 *
156177 * @param executor
157178 * the new executor
@@ -160,6 +181,26 @@ public static void setExecutor(Executor executor) {
160181 BackgroundExecutor .executor = executor ;
161182 }
162183
184+ /**
185+ * Cancel all tasks having the specified <code>id</code>.
186+ *
187+ * @param id
188+ * the cancellation identifier
189+ * @param mayInterruptIfRunning
190+ * <code>true<.code> if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete
191+ */
192+ public static synchronized void cancelAll (String id , boolean mayInterruptIfRunning ) {
193+ for (int i = tasks .size () - 1 ; i >= 0 ; i --) {
194+ Task task = tasks .get (i );
195+ if (id .equals (task .id )) {
196+ tasks .remove (i );
197+ if (task .future != null ) {
198+ task .future .cancel (mayInterruptIfRunning );
199+ }
200+ }
201+ }
202+ }
203+
163204 /**
164205 * Indicates whether a task with the specified <code>serial</code> has been
165206 * submitted to the executor.
@@ -198,12 +239,17 @@ private static Task take(String serial) {
198239
199240 public static abstract class Task implements Runnable {
200241
242+ private String id ;
201243 private int delay ;
202244 private long targetTime ; /* in milliseconds since epoch */
203245 private String serial ;
204246 private boolean executionAsked ;
247+ private Future <?> future ;
205248
206- public Task (int delay , String serial ) {
249+ public Task (String id , int delay , String serial ) {
250+ if (!"" .equals (id )) {
251+ this .id = id ;
252+ }
207253 if (delay > 0 ) {
208254 this .delay = delay ;
209255 targetTime = System .currentTimeMillis () + delay ;
@@ -226,22 +272,24 @@ public void run() {
226272 public abstract void execute ();
227273
228274 private void postExecute () {
229- if (serial == null ) {
275+ if (id == null && serial == null ) {
230276 /* nothing to do */
231277 return ;
232278 }
233279 synchronized (BackgroundExecutor .class ) {
234280 /* execution complete */
235281 tasks .remove (this );
236282
237- Task next = take (serial );
238- if (next != null ) {
239- if (next .delay != 0 ) {
240- /* compute remaining delay */
241- next .delay = Math .max (0 , (int ) (targetTime - System .currentTimeMillis ()));
283+ if (serial != null ) {
284+ Task next = take (serial );
285+ if (next != null ) {
286+ if (next .delay != 0 ) {
287+ /* compute remaining delay */
288+ next .delay = Math .max (0 , (int ) (targetTime - System .currentTimeMillis ()));
289+ }
290+ /* a task having the same serial was queued, execute it */
291+ BackgroundExecutor .execute (next );
242292 }
243- /* a task having the same serial was queued, execute it */
244- BackgroundExecutor .execute (next );
245293 }
246294 }
247295 }
0 commit comments