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 * @return Actual scheduled task; use this if you need to cancel 167 */ 168 public static @Nonnull TimerTask scheduleOnGUIThread(@Nonnull TimerTask task, @Nonnull Date time) { 169 synchronized (commonTimer) { 170 var gtask = gtask(task); 171 try { 172 commonTimer.schedule(gtask, time); 173 } catch (IllegalStateException e) { 174 log.warn("During schedule()", e); 175 } 176 return gtask; 177 } 178 } 179 180 /** 181 * Schedules the specified task for repeated <i>fixed-delay execution</i> 182 * on the GUI Thread, beginning at the specified time. 183 * Subsequent executions take place at approximately regular intervals, 184 * separated by the specified period. 185 * @param task task to be scheduled. 186 * @param firstTime First time at which task is to be executed. 187 * @param period time in milliseconds between successive task executions. 188 * @return Actual scheduled task; use this if you need to cancel 189 */ 190 public static @Nonnull TimerTask scheduleOnGUIThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) { 191 synchronized (commonTimer) { 192 var gtask = gtask(task); 193 try { 194 commonTimer.schedule(gtask, firstTime, period); 195 } catch (IllegalStateException e) { 196 log.warn("During schedule()", e); 197 } 198 return gtask; 199 } 200 } 201 202 /** 203 * Schedules the specified task for execution on the GUI Thread 204 * after the specified delay. 205 * @param task task to be scheduled. 206 * @param delay delay in milliseconds before task is to be executed. 207 * @return Actual scheduled task; use this if you need to cancel 208 */ 209 public static @Nonnull TimerTask scheduleOnGUIThread(@Nonnull TimerTask task, long delay) { 210 synchronized (commonTimer) { 211 var gtask = gtask(task); 212 try { 213 commonTimer.schedule(gtask, delay); 214 } catch (IllegalStateException e) { 215 log.warn("During schedule()", e); 216 } 217 return gtask; 218 } 219 } 220 221 /** 222 * Schedules the specified task for repeated <i>fixed-delay execution</i> 223 * on the GUI Thread, beginning after the specified delay. 224 * Subsequent executions take place at approximately regular intervals 225 * separated by the specified period. 226 * @param task task to be scheduled. 227 * @param delay delay in milliseconds before task is to be executed. 228 * @param period time in milliseconds between successive task executions. 229 * @return Actual scheduled task; use this if you need to cancel 230 */ 231 public static @Nonnull TimerTask scheduleOnGUIThread(@Nonnull TimerTask task, long delay, long period) { 232 synchronized (commonTimer) { 233 var gtask = gtask(task); 234 try { 235 commonTimer.schedule(gtask, delay, period); 236 } catch (IllegalStateException e) { 237 log.warn("During schedule()", e); 238 } 239 return gtask; 240 } 241 } 242 243 /** 244 * Schedules the specified task for repeated <i>fixed-delay execution</i>, 245 * on the GUI Thread, beginning at the specified time. 246 * Subsequent executions take place at approximately regular intervals, 247 * separated by the specified period. 248 * @param task task to be scheduled. 249 * @param firstTime First time at which task is to be executed. 250 * @param period time in milliseconds between successive task executions. 251 * @return Actual scheduled task; use this if you need to cancel 252 */ 253 public static @Nonnull TimerTask scheduleAtFixedRateOnGUIThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) { 254 synchronized (commonTimer) { 255 var gtask = gtask(task); 256 try { 257 commonTimer.schedule(gtask, firstTime, period); 258 } catch (IllegalStateException e) { 259 log.warn("During schedule()", e); 260 } 261 return gtask; 262 } 263 } 264 265 /** 266 * Schedules the specified task for repeated <i>fixed-delay execution</i> 267 * on the GUI Thread beginning after the specified delay. 268 * Subsequent executions take place at approximately regular intervals 269 * separated by the specified period. 270 * @param task task to be scheduled. 271 * @param delay delay in milliseconds before task is to be executed. 272 * @param period time in milliseconds between successive task executions. 273 * @return Actual scheduled task; use this if you need to cancel 274 */ 275 public static @Nonnull TimerTask scheduleAtFixedRateOnGUIThread(@Nonnull TimerTask task, long delay, long period) { 276 synchronized (commonTimer) { 277 var gtask = gtask(task); 278 try { 279 commonTimer.schedule(gtask(task), delay, period); 280 } catch (IllegalStateException e) { 281 log.warn("During schedule()", e); 282 } 283 return gtask; 284 } 285 } 286 287 288 // arrange to run on layout thread 289 private static TimerTask ltask(TimerTask task) { 290 return new TimerTask(){ 291 @Override 292 public void run() { 293 ThreadingUtil.runOnLayoutEventually(() -> {task.run();}); 294 } 295 }; 296 } 297 298 /** 299 * Schedule a TimerTask on Layout Thread for execution at the specified time. 300 * If time is in the past, the task is scheduled for immediate execution. 301 * @param task task to be scheduled. 302 * @param time time at which task is to be executed. 303 */ 304 public static void scheduleOnLayoutThread(@Nonnull TimerTask task, @Nonnull Date time) { 305 synchronized (commonTimer) { 306 try { 307 commonTimer.schedule(ltask(task), time); 308 } catch (IllegalStateException e) { 309 log.warn("During schedule()", e); 310 } 311 } 312 } 313 314 /** 315 * Schedules the specified task for repeated <i>fixed-delay execution</i> 316 * on the Layout Thread, beginning at the specified time. 317 * Subsequent executions take place at approximately regular intervals, 318 * separated by the specified period. 319 * @param task task to be scheduled. 320 * @param firstTime First time at which task is to be executed. 321 * @param period time in milliseconds between successive task executions. 322 */ 323 public static void scheduleOnLayoutThread(@Nonnull TimerTask task, @Nonnull Date firstTime, long period) { 324 synchronized (commonTimer) { 325 try { 326 commonTimer.schedule(ltask(task), firstTime, period); 327 } catch (IllegalStateException e) { 328 log.warn("During schedule()", e); 329 } 330 } 331 } 332 333 /** 334 * Schedules the specified task for execution on the Layout Thread 335 * after the specified delay. 336 * @param task task to be scheduled. 337 * @param delay delay in milliseconds before task is to be executed. 338 */ 339 public static void scheduleOnLayoutThread(@Nonnull TimerTask task, long delay) { 340 synchronized (commonTimer) { 341 try { 342 commonTimer.schedule(ltask(task), delay); 343 } catch (IllegalStateException e) { 344 log.warn("During schedule()", e); 345 } 346 } 347 } 348 349 /** 350 * Schedules the specified task for repeated <i>fixed-delay execution</i> 351 * on the Layout Thread beginning after the specified delay. 352 * Subsequent executions take place at approximately regular intervals 353 * separated by the specified period. 354 * @param task task to be scheduled. 355 * @param delay delay in milliseconds before task is to be executed. 356 * @param period time in milliseconds between successive task executions. 357 */ 358 public static void scheduleOnLayoutThread(@Nonnull TimerTask task, long delay, long period) { 359 synchronized (commonTimer) { 360 try { 361 commonTimer.schedule(ltask(task), delay, period); 362 } catch (IllegalStateException e) { 363 log.warn("During schedule()", e); 364 } 365 } 366 } 367 368 /** 369 * Schedules the specified task for repeated <i>fixed-delay execution</i>, 370 * on the Layout Thread, beginning at the specified time. 371 * Subsequent executions take place at approximately regular intervals, 372 * separated by the specified period. 373 * @param task task to be scheduled. 374 * @param firstTime First time at which task is to be executed. 375 * @param period time in milliseconds between successive task executions. 376 */ 377 public static void scheduleAtFixedRateOnLayoutThread( 378 @Nonnull TimerTask task, @Nonnull Date firstTime, long period) { 379 synchronized (commonTimer) { 380 try { 381 commonTimer.schedule(ltask(task), firstTime, period); 382 } catch (IllegalStateException e) { 383 log.warn("During schedule()", e); 384 } 385 } 386 } 387 388 /** 389 * Schedules the specified task for repeated <i>fixed-delay execution</i> 390 * on the Layout Thread beginning after the specified delay. 391 * Subsequent executions take place at approximately regular intervals 392 * separated by the specified period. 393 * @param task task to be scheduled. 394 * @param delay delay in milliseconds before task is to be executed. 395 * @param period time in milliseconds between successive task executions. 396 */ 397 public static void scheduleAtFixedRateOnLayoutThread(@Nonnull TimerTask task, long delay, long period) { 398 synchronized (commonTimer) { 399 try { 400 commonTimer.schedule(ltask(task), delay, period); 401 } catch (IllegalStateException e) { 402 log.warn("During schedule()", e); 403 } 404 } 405 } 406 407 static final Timer commonTimer = new Timer("JMRI Common Timer", true); 408 409 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TimerUtil.class); 410}