001package jmri.util;
002
003import java.util.Date;
004import java.util.Timer;
005import java.util.TimerTask;
006import javax.annotation.Nonnull;
007
008
009/**
010 * Common utility methods for working with (@link java.util.Timer)
011 * <p>
012 * Each {@link java.util.Timer} uses a thread, which means that they're
013 * not throw-away timers:  You either track when you can destroy them
014 * (and that destruction is not obvious), or they stick around consuming
015 * resources.
016 * <p>
017 * This class provides most of the functionality of a Timer.
018 * Some differences:
019 * <ul>
020 * <li>When migrating code that uses Timer.cancel() to end operation, you have to
021 *     retain references to the individual TimerTask objects and cancel them instead.
022 * </ul>
023 * <p>
024 * For convenience, this also provides methods to ensure that the task is invoked
025 * on a specific JMRI thread.
026 * <p>
027 * Please note the comment in the {@link Timer} Javadoc about how
028 * {@link java.util.concurrent.ScheduledThreadPoolExecutor} might provide a better
029 * underlying implementation.
030 * Method JavaDoc tweaked from java.util.Timer.
031 * @author Bob Jacobsen Copyright 2018
032 */
033@javax.annotation.concurrent.Immutable
034public final class TimerUtil {
035
036    // class only supplies static methods
037    private TimerUtil() {}
038
039    // Timer implementation methods
040
041    /**
042     * Schedule a TimerTask for execution at the specified time.
043     * If time is in the past, the task is scheduled for immediate execution.
044     * @param task task to be scheduled.
045     * @param time time at which task is to be executed.
046     */
047    public static void schedule(@Nonnull TimerTask task, @Nonnull Date time) {
048        synchronized (commonTimer) {
049            try {
050                commonTimer.schedule(task, time);
051            } catch (IllegalStateException e) {
052                log.warn("During schedule()", e);
053            }
054        }
055    }
056
057    /**
058     * Schedules the specified task for repeated <i>fixed-delay execution</i>,
059     * beginning at the specified time.
060     * Subsequent executions take place at approximately regular intervals,
061     * separated by the specified period.
062     * @param task   task to be scheduled.
063     * @param firstTime First time at which task is to be executed.
064     * @param period time in milliseconds between successive task executions.
065     */
066    public static void schedule(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
067        synchronized (commonTimer) {
068            try {
069                commonTimer.schedule(task, firstTime, period);
070            } catch (IllegalStateException e) {
071                log.warn("During schedule()", e);
072            }
073        }
074    }
075
076    /**
077     * Schedules the specified task for execution after the specified delay.
078     * @param task  task to be scheduled.
079     * @param delay delay in milliseconds before task is to be executed.
080     */
081    public static void schedule(@Nonnull TimerTask task, long delay) {
082        synchronized (commonTimer) {
083            try {
084                commonTimer.schedule(task, delay);
085            } catch (IllegalStateException e) {
086                log.warn("During schedule()", e);
087            }
088        }
089    }
090
091    /**
092     * Schedules the specified task for repeated <i>fixed-delay execution</i>,
093     * beginning after the specified delay.
094     * Subsequent executions take place at approximately regular intervals
095     * separated by the specified period.
096     * @param task   task to be scheduled.
097     * @param delay  delay in milliseconds before task is to be executed.
098     * @param period time in milliseconds between successive task executions.
099     */
100    public static void schedule(@Nonnull TimerTask task, long delay, long period) {
101        synchronized (commonTimer) {
102            try {
103                commonTimer.schedule(task, delay, period);
104            } catch (IllegalStateException e) {
105                log.warn("During schedule()", e);
106            }
107        }
108    }
109
110    /**
111     * Schedules the specified task for repeated <i>fixed-delay execution</i>,
112     * beginning at the specified time.
113     * Subsequent executions take place at approximately regular intervals,
114     * separated by the specified period.
115     * @param task   task to be scheduled.
116     * @param firstTime First time at which task is to be executed.
117     * @param period time in milliseconds between successive task executions.
118     */
119    public static void scheduleAtFixedRate(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
120        synchronized (commonTimer) {
121            try {
122                commonTimer.schedule(task, firstTime, period);
123            } catch (IllegalStateException e) {
124                log.warn("During schedule()", e);
125            }
126        }
127    }
128
129    /**
130     * Schedules the specified task for repeated <i>fixed-delay execution</i>,
131     * beginning after the specified delay.
132     * Subsequent executions take place at approximately regular intervals
133     * separated by the specified period.
134     * @param task   task to be scheduled.
135     * @param delay  delay in milliseconds before task is to be executed.
136     * @param period time in milliseconds between successive task executions.
137     */
138    public static void scheduleAtFixedRate(@Nonnull TimerTask task, long delay, long period) {
139        synchronized (commonTimer) {
140            try {
141                commonTimer.schedule(task, delay, period);
142            } catch (IllegalStateException e) {
143                log.warn("During schedule()", e);
144            }
145        }
146    }
147
148
149    // GUI-thread implementation methods
150
151    // arrange to run on GUI thread
152    private static TimerTask gtask(TimerTask task) {
153        return new TimerTask(){
154                @Override
155                public void run() {
156                    ThreadingUtil.runOnGUIEventually(() -> {task.run();});
157                }
158        };
159    }
160
161    /**
162     * Schedule a TimerTask on GUI Thread for execution at the specified time.
163     * If time is in the past, the task is scheduled for immediate execution.
164     * @param task task to be scheduled.
165     * @param time time at which task is to be executed.
166     */
167    public static void scheduleOnGUIThread(@Nonnull TimerTask task, @Nonnull Date time) {
168        synchronized (commonTimer) {
169            try {
170                commonTimer.schedule(gtask(task), time);
171            } catch (IllegalStateException e) {
172                log.warn("During schedule()", e);
173            }
174        }
175    }
176
177    /**
178     * Schedules the specified task for repeated <i>fixed-delay execution</i>
179     * on the GUI Thread, beginning at the specified time.
180     * Subsequent executions take place at approximately regular intervals,
181     * separated by the specified period.
182     * @param task   task to be scheduled.
183     * @param firstTime First time at which task is to be executed.
184     * @param period time in milliseconds between successive task executions.
185     */
186    public static void scheduleOnGUIThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
187        synchronized (commonTimer) {
188            try {
189                commonTimer.schedule(gtask(task), firstTime, period);
190            } catch (IllegalStateException e) {
191                log.warn("During schedule()", e);
192            }
193        }
194    }
195
196    /**
197     * Schedules the specified task for execution on the GUI Thread
198     * after the specified delay.
199     * @param task  task to be scheduled.
200     * @param delay delay in milliseconds before task is to be executed.
201     */
202    public static void scheduleOnGUIThread(@Nonnull TimerTask task, long delay) {
203        synchronized (commonTimer) {
204            try {
205                commonTimer.schedule(gtask(task), delay);
206            } catch (IllegalStateException e) {
207                log.warn("During schedule()", e);
208            }
209        }
210    }
211
212    /**
213     * Schedules the specified task for repeated <i>fixed-delay execution</i>
214     * on the GUI Thread, beginning after the specified delay.
215     * Subsequent executions take place at approximately regular intervals
216     * separated by the specified period.
217     * @param task   task to be scheduled.
218     * @param delay  delay in milliseconds before task is to be executed.
219     * @param period time in milliseconds between successive task executions.
220     */
221    public static void scheduleOnGUIThread(@Nonnull TimerTask task, long delay, long period) {
222        synchronized (commonTimer) {
223            try {
224                commonTimer.schedule(gtask(task), delay, period);
225            } catch (IllegalStateException e) {
226                log.warn("During schedule()", e);
227            }
228        }
229    }
230
231    /**
232     * Schedules the specified task for repeated <i>fixed-delay execution</i>,
233     * on the GUI Thread, beginning at the specified time.
234     * Subsequent executions take place at approximately regular intervals,
235     * separated by the specified period.
236     * @param task   task to be scheduled.
237     * @param firstTime First time at which task is to be executed.
238     * @param period time in milliseconds between successive task executions.
239     */
240    public static void scheduleAtFixedRateOnGUIThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
241        synchronized (commonTimer) {
242            try {
243                commonTimer.schedule(gtask(task), firstTime, period);
244            } catch (IllegalStateException e) {
245                log.warn("During schedule()", e);
246            }
247        }
248    }
249
250    /**
251     * Schedules the specified task for repeated <i>fixed-delay execution</i>
252     * on the GUI Thread beginning after the specified delay.
253     * Subsequent executions take place at approximately regular intervals
254     * separated by the specified period.
255     * @param task   task to be scheduled.
256     * @param delay  delay in milliseconds before task is to be executed.
257     * @param period time in milliseconds between successive task executions.
258     */
259    public static void scheduleAtFixedRateOnGUIThread(@Nonnull TimerTask task, long delay, long period) {
260        synchronized (commonTimer) {
261            try {
262                commonTimer.schedule(gtask(task), delay, period);
263            } catch (IllegalStateException e) {
264                log.warn("During schedule()", e);
265            }
266        }
267    }
268
269
270    // arrange to run on layout thread
271    private static TimerTask ltask(TimerTask task) {
272        return new TimerTask(){
273                @Override
274                public void run() {
275                    ThreadingUtil.runOnLayoutEventually(() -> {task.run();});
276                }
277        };
278    }
279
280    /**
281     * Schedule a TimerTask on Layout Thread for execution at the specified time.
282     * If time is in the past, the task is scheduled for immediate execution.
283     * @param task task to be scheduled.
284     * @param time time at which task is to be executed.
285     */
286    public static void scheduleOnLayoutThread(@Nonnull TimerTask task, @Nonnull Date time) {
287        synchronized (commonTimer) {
288            try {
289                commonTimer.schedule(ltask(task), time);
290            } catch (IllegalStateException e) {
291                log.warn("During schedule()", e);
292            }
293        }
294    }
295
296    /**
297     * Schedules the specified task for repeated <i>fixed-delay execution</i>
298     * on the Layout Thread, beginning at the specified time.
299     * Subsequent executions take place at approximately regular intervals,
300     * separated by the specified period.
301     * @param task   task to be scheduled.
302     * @param firstTime First time at which task is to be executed.
303     * @param period time in milliseconds between successive task executions.
304     */
305    public static void scheduleOnLayoutThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
306        synchronized (commonTimer) {
307            try {
308                commonTimer.schedule(ltask(task), firstTime, period);
309            } catch (IllegalStateException e) {
310                log.warn("During schedule()", e);
311            }
312        }
313    }
314
315    /**
316     * Schedules the specified task for execution on the Layout Thread
317     * after the specified delay.
318     * @param task  task to be scheduled.
319     * @param delay delay in milliseconds before task is to be executed.
320     */
321    public static void scheduleOnLayoutThread(@Nonnull TimerTask task, long delay) {
322        synchronized (commonTimer) {
323            try {
324                commonTimer.schedule(ltask(task), delay);
325            } catch (IllegalStateException e) {
326                log.warn("During schedule()", e);
327            }
328        }
329    }
330
331    /**
332     * Schedules the specified task for repeated <i>fixed-delay execution</i>
333     * on the Layout Thread beginning after the specified delay.
334     * Subsequent executions take place at approximately regular intervals
335     * separated by the specified period.
336     * @param task   task to be scheduled.
337     * @param delay  delay in milliseconds before task is to be executed.
338     * @param period time in milliseconds between successive task executions.
339     */
340    public static void scheduleOnLayoutThread(@Nonnull TimerTask task, long delay, long period) {
341        synchronized (commonTimer) {
342            try {
343                commonTimer.schedule(ltask(task), delay, period);
344            } catch (IllegalStateException e) {
345                log.warn("During schedule()", e);
346            }
347        }
348    }
349
350    /**
351     * Schedules the specified task for repeated <i>fixed-delay execution</i>,
352     * on the Layout Thread, beginning at the specified time.
353     * Subsequent executions take place at approximately regular intervals,
354     * separated by the specified period.
355     * @param task   task to be scheduled.
356     * @param firstTime First time at which task is to be executed.
357     * @param period time in milliseconds between successive task executions.
358     */
359    public static void scheduleAtFixedRateOnLayoutThread(
360        @Nonnull TimerTask task, @Nonnull Date firstTime, long period) {
361        synchronized (commonTimer) {
362            try {
363                commonTimer.schedule(ltask(task), firstTime, period);
364            } catch (IllegalStateException e) {
365                log.warn("During schedule()", e);
366            }
367        }
368    }
369
370    /**
371     * Schedules the specified task for repeated <i>fixed-delay execution</i>
372     * on the Layout Thread beginning after the specified delay.
373     * Subsequent executions take place at approximately regular intervals
374     * separated by the specified period.
375     * @param task   task to be scheduled.
376     * @param delay  delay in milliseconds before task is to be executed.
377     * @param period time in milliseconds between successive task executions.
378     */
379    public static void scheduleAtFixedRateOnLayoutThread(@Nonnull TimerTask task, long delay, long period) {
380        synchronized (commonTimer) {
381            try {
382                commonTimer.schedule(ltask(task), delay, period);
383            } catch (IllegalStateException e) {
384                log.warn("During schedule()", e);
385            }
386        }
387    }
388
389
390    static final Timer commonTimer = new Timer("JMRI Common Timer", true);
391
392    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TimerUtil.class);
393}