From a8614533bbb3802d37ce7c3c9c1d0f7bced7a098 Mon Sep 17 00:00:00 2001
From: Magnus Reftel execute(..) signals that it has noticed the set flag.
*
- * If the Job performs some form of blocking I/O or similar functions, you may want to consider having the Job.execute(..) method store a
- * reference to the calling Thread as a member variable. Then the Implementation of this interfaces interrupt() method can
- * call interrupt() on that Thread. Before attempting this, make sure that you fully understand what
- * java.lang.Thread.interrupt() does and doesn't do. Also make sure that you clear the Job's member reference to the Thread when the
- * execute(..) method exits (preferably in a finally block.
+ * If the Job performs some form of blocking I/O or similar functions, you may want to consider extending InterruptingJob. Its
+ * interrupt() method calls interrupt() on the thread that is executing the job. Before attempting this, make sure that you
+ * fully understand what java.lang.Thread.interrupt() does and doesn't do.
*
- * See Example 7 (org.quartz.examples.example7.DumbInterruptableJob) for a simple implementation demonstration. - *
- * + * * @see Job * @see StatefulJob * @see Scheduler#interrupt(JobKey) diff --git a/src/test/java/org/knowm/sundial/InterruptingJobTest.java b/src/test/java/org/knowm/sundial/InterruptingJobTest.java new file mode 100644 index 0000000..87a0943 --- /dev/null +++ b/src/test/java/org/knowm/sundial/InterruptingJobTest.java @@ -0,0 +1,74 @@ +package org.knowm.sundial; + +import java.util.concurrent.Semaphore; + +import org.junit.Test; +import org.quartz.core.JobExecutionContext; +import org.quartz.core.JobExecutionContextImpl; +import org.quartz.core.TriggerFiredBundle; +import org.quartz.jobs.JobDetail; +import org.quartz.jobs.JobDetailImpl; +import org.quartz.triggers.OperableTrigger; +import org.quartz.triggers.SimpleTriggerImpl; + +public class InterruptingJobTest { + @Test + public void shouldInterruptThread() throws Exception { + final Semaphore sem = new Semaphore(0); + final WaitingJob job = new WaitingJob(sem); + + final JobDetail detail = new JobDetailImpl(); + final OperableTrigger trigger = new SimpleTriggerImpl(); + final TriggerFiredBundle bundle = new TriggerFiredBundle(detail, trigger, null, true, null, null, null, null); + final JobExecutionContext context = new JobExecutionContextImpl(null, bundle, job); + + final Thread t = new Thread(new Runnable() { + @Override + public void run() { + try { + job.execute(context); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + t.start(); + + synchronized (job) { + while (!job.aboutToSleep) { + job.wait(); + } + } + while (!sem.hasQueuedThreads()) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + job.interrupt(); + + t.join(); + } + + static class WaitingJob extends InterruptingJob { + public boolean aboutToSleep = false; + final Semaphore sem; + public WaitingJob(Semaphore sem) { + this.sem = sem; + } + @Override + public void doRun() { + synchronized (this) { + aboutToSleep = true; + notify(); + } + try { + sem.acquire(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } +}