JWS-121 Added new scheduler utility classes so that usage statistics and service...
authorFábio Madeira <fmmarquesmadeira@dundee.ac.uk>
Fri, 2 Jun 2017 14:00:56 +0000 (15:00 +0100)
committerFábio Madeira <fmmarquesmadeira@dundee.ac.uk>
Fri, 2 Jun 2017 14:00:56 +0000 (15:00 +0100)
webservices/compbio/stat/servlet/util/RefreshIterator.java [new file with mode: 0644]
webservices/compbio/stat/servlet/util/ScheduleIterator.java [new file with mode: 0644]
webservices/compbio/stat/servlet/util/Scheduler.java [new file with mode: 0644]
webservices/compbio/stat/servlet/util/SchedulerTask.java [new file with mode: 0644]

diff --git a/webservices/compbio/stat/servlet/util/RefreshIterator.java b/webservices/compbio/stat/servlet/util/RefreshIterator.java
new file mode 100644 (file)
index 0000000..37cd569
--- /dev/null
@@ -0,0 +1,36 @@
+package compbio.stat.servlet.util;
+
+
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * A <code>RefreshIterator</code> returns a sequence of dates on subsequent days
+ * representing the same time each day.
+ */
+public class RefreshIterator implements ScheduleIterator {
+    private final int hourOfDay, minute, second, freq_time;
+    private final Calendar calendar = Calendar.getInstance();
+
+    public RefreshIterator(int hourOfDay, int minute, int second, int freq_time) {
+        this(hourOfDay, minute, second, freq_time, new Date());
+    }
+
+    public RefreshIterator(int hourOfDay, int minute, int second, int freq_time, Date date) {
+        this.hourOfDay = hourOfDay;
+        this.minute = minute;
+        this.second = second;
+        this.freq_time = freq_time;
+        calendar.setTime(date);
+        calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
+        calendar.set(Calendar.MINUTE, minute);
+        calendar.set(Calendar.SECOND, second);
+        calendar.set(Calendar.MILLISECOND, 0);
+    }
+
+    public Date next() {
+        calendar.add(Calendar.MINUTE, this.freq_time);
+        return calendar.getTime();
+    }
+
+}
diff --git a/webservices/compbio/stat/servlet/util/ScheduleIterator.java b/webservices/compbio/stat/servlet/util/ScheduleIterator.java
new file mode 100644 (file)
index 0000000..7016520
--- /dev/null
@@ -0,0 +1,18 @@
+package compbio.stat.servlet.util;
+
+import java.util.Date;
+
+/**
+ * Implementations of <code>ScheduleIterator</code> specify a schedule as
+ * a series of <code>java.util.Date</code> objects.
+ */
+
+public interface ScheduleIterator {
+
+    /**
+     * Returns the next time that the related {@link SchedulerTask} should be run.
+     *
+     * @return the next time of execution
+     */
+    public Date next();
+}
diff --git a/webservices/compbio/stat/servlet/util/Scheduler.java b/webservices/compbio/stat/servlet/util/Scheduler.java
new file mode 100644 (file)
index 0000000..3594179
--- /dev/null
@@ -0,0 +1,108 @@
+package compbio.stat.servlet.util;
+
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
+
+/**
+ * A facility for threads to schedule recurring tasks for future
+ * execution in a background thread.
+ * <p>
+ * This class is thread-safe: multiple threads can share a single
+ * <code>Scheduler</code> object without the need for external synchronization.
+ * <p>
+ * Implementation note: internally <code>Scheduler</code> uses a
+ * <code>java.util.Timer</code> to schedule tasks.
+ */
+public class Scheduler {
+
+    class SchedulerTimerTask extends TimerTask {
+        private SchedulerTask schedulerTask;
+        private ScheduleIterator iterator;
+
+        public SchedulerTimerTask(SchedulerTask schedulerTask,
+                                  ScheduleIterator iterator) {
+            this.schedulerTask = schedulerTask;
+            this.iterator = iterator;
+        }
+
+        public void run() {
+            schedulerTask.run();
+            reschedule(schedulerTask, iterator);
+        }
+    }
+
+    private final Timer timer = new Timer();
+
+    public Scheduler() {
+    }
+
+    /**
+     * Terminates this <code>Scheduler</code>, discarding any currently scheduled tasks.
+     * Does not interfere with a currently executing task (if it exists). Once a scheduler
+     * has been terminated, its execution thread terminates gracefully, and no more tasks
+     * may be scheduled on it.
+     * <p>
+     * Note that calling this method from within the run method of a scheduler task that was
+     * invoked by this scheduler absolutely guarantees that the ongoing task execution is the
+     * last task execution that will ever be performed by this scheduler.
+     * <p>
+     * This method may be called repeatedly; the second and subsequent calls have no effect.
+     */
+
+    public void cancel() {
+        timer.cancel();
+    }
+
+    /**
+     * Schedules the specified task for execution according to the specified schedule. If times
+     * specified by the <code>ScheduleIterator</code> are in the past they are scheduled for
+     * immediate execution.
+     * <p>
+     *
+     * @param schedulerTask task to be scheduled
+     * @param iterator      iterator that describes the schedule
+     * @throws IllegalStateException if task was already scheduled or cancelled, scheduler was
+     *                               cancelled, or scheduler thread terminated.
+     */
+
+    public void schedule(SchedulerTask schedulerTask,
+                         ScheduleIterator iterator) {
+
+        Date time = iterator.next();
+        if (time == null) {
+            schedulerTask.cancel();
+        } else {
+            synchronized (schedulerTask.lock) {
+                if (schedulerTask.state != SchedulerTask.VIRGIN) {
+                    throw new IllegalStateException("Task already scheduled " +
+                            "or cancelled");
+                }
+                schedulerTask.state = SchedulerTask.SCHEDULED;
+                schedulerTask.timerTask =
+                        new SchedulerTimerTask(schedulerTask, iterator);
+                timer.schedule(schedulerTask.timerTask, time);
+            }
+        }
+    }
+
+    private void reschedule(SchedulerTask schedulerTask,
+                            ScheduleIterator iterator) {
+
+        Date time = iterator.next();
+        if (time == null) {
+            schedulerTask.cancel();
+        } else {
+            synchronized (schedulerTask.lock) {
+                if (schedulerTask.state != SchedulerTask.CANCELLED) {
+                    schedulerTask.timerTask =
+                            new SchedulerTimerTask(schedulerTask, iterator);
+                    timer.schedule(schedulerTask.timerTask, time);
+                }
+            }
+        }
+    }
+
+}
+
diff --git a/webservices/compbio/stat/servlet/util/SchedulerTask.java b/webservices/compbio/stat/servlet/util/SchedulerTask.java
new file mode 100644 (file)
index 0000000..fa079ab
--- /dev/null
@@ -0,0 +1,68 @@
+package compbio.stat.servlet.util;
+
+import java.util.TimerTask;
+
+
+/**
+ * A task that can be scheduled for recurring execution by a {@link Scheduler}.
+ */
+public abstract class SchedulerTask implements Runnable {
+
+    final Object lock = new Object();
+
+    int state = VIRGIN;
+    static final int VIRGIN = 0;
+    static final int SCHEDULED = 1;
+    static final int CANCELLED = 2;
+
+    TimerTask timerTask;
+
+    /**
+     * Creates a new scheduler task.
+     */
+
+    protected SchedulerTask() {
+    }
+
+    /**
+     * The action to be performed by this scheduler task.
+     */
+
+    public abstract void run();
+
+    /**
+     * Cancels this scheduler task.
+     * <p>
+     * This method may be called repeatedly; the second and subsequent calls have no effect.
+     *
+     * @return true if this task was already scheduled to run
+     */
+
+    public boolean cancel() {
+        synchronized (lock) {
+            if (timerTask != null) {
+                timerTask.cancel();
+            }
+            boolean result = (state == SCHEDULED);
+            state = CANCELLED;
+            return result;
+        }
+    }
+
+    /**
+     * Returns the <i>scheduled</i> execution time of the most recent actual execution of
+     * this task. (If this method is invoked while task execution is in progress,
+     * the return value is the scheduled execution time of the ongoing task execution.)
+     *
+     * @return the time at which the most recent execution of this task was scheduled
+     * to occur, in the format returned by <code>Date.getTime()</code>. The return value
+     * is undefined if the task has yet to commence its first execution.
+     */
+
+    public long scheduledExecutionTime() {
+        synchronized (lock) {
+            return timerTask == null ? 0 : timerTask.scheduledExecutionTime();
+        }
+    }
+
+}