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}