View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  
27  
28  package javax.swing;
29  
30  
31  
32  import java.util.*;
33  import java.util.concurrent.atomic.AtomicBoolean;
34  import java.util.concurrent.locks.*;
35  import java.awt.*;
36  import java.awt.event.*;
37  import java.io.Serializable;
38  import java.io.*;
39  import java.security.AccessControlContext;
40  import java.security.AccessController;
41  import java.security.PrivilegedAction;
42  import javax.swing.event.EventListenerList;
43  
44  
45  
46  /**
47   * Fires one or more {@code ActionEvent}s at specified
48   * intervals. An example use is an animation object that uses a
49   * <code>Timer</code> as the trigger for drawing its frames.
50   *<p>
51   * Setting up a timer
52   * involves creating a <code>Timer</code> object,
53   * registering one or more action listeners on it,
54   * and starting the timer using
55   * the <code>start</code> method.
56   * For example,
57   * the following code creates and starts a timer
58   * that fires an action event once per second
59   * (as specified by the first argument to the <code>Timer</code> constructor).
60   * The second argument to the <code>Timer</code> constructor
61   * specifies a listener to receive the timer's action events.
62   *
63   *<pre>
64   *  int delay = 1000; //milliseconds
65   *  ActionListener taskPerformer = new ActionListener() {
66   *      public void actionPerformed(ActionEvent evt) {
67   *          <em>//...Perform a task...</em>
68   *      }
69   *  };
70   *  new Timer(delay, taskPerformer).start();</pre>
71   *
72   * <p>
73   * {@code Timers} are constructed by specifying both a delay parameter
74   * and an {@code ActionListener}. The delay parameter is used
75   * to set both the initial delay and the delay between event
76   * firing, in milliseconds. Once the timer has been started,
77   * it waits for the initial delay before firing its
78   * first <code>ActionEvent</code> to registered listeners.
79   * After this first event, it continues to fire events
80   * every time the between-event delay has elapsed, until it
81   * is stopped.
82   * <p>
83   * After construction, the initial delay and the between-event
84   * delay can be changed independently, and additional
85   * <code>ActionListeners</code> may be added.
86   * <p>
87   * If you want the timer to fire only the first time and then stop,
88   * invoke <code>setRepeats(false)</code> on the timer.
89   * <p>
90   * Although all <code>Timer</code>s perform their waiting
91   * using a single, shared thread
92   * (created by the first <code>Timer</code> object that executes),
93   * the action event handlers for <code>Timer</code>s
94   * execute on another thread -- the event-dispatching thread.
95   * This means that the action handlers for <code>Timer</code>s
96   * can safely perform operations on Swing components.
97   * However, it also means that the handlers must execute quickly
98   * to keep the GUI responsive.
99   *
100  * <p>
101  * In v 1.3, another <code>Timer</code> class was added
102  * to the Java platform: <code>java.util.Timer</code>.
103  * Both it and <code>javax.swing.Timer</code>
104  * provide the same basic functionality,
105  * but <code>java.util.Timer</code>
106  * is more general and has more features.
107  * The <code>javax.swing.Timer</code> has two features
108  * that can make it a little easier to use with GUIs.
109  * First, its event handling metaphor is familiar to GUI programmers
110  * and can make dealing with the event-dispatching thread
111  * a bit simpler.
112  * Second, its
113  * automatic thread sharing means that you don't have to
114  * take special steps to avoid spawning
115  * too many threads.
116  * Instead, your timer uses the same thread
117  * used to make cursors blink,
118  * tool tips appear,
119  * and so on.
120  *
121  * <p>
122  * You can find further documentation
123  * and several examples of using timers by visiting
124  * <a href="http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html"
125  * target = "_top">How to Use Timers</a>,
126  * a section in <em>The Java Tutorial.</em>
127  * For more examples and help in choosing between
128  * this <code>Timer</code> class and
129  * <code>java.util.Timer</code>,
130  * see
131  * <a href="http://java.sun.com/products/jfc/tsc/articles/timer/"
132  * target="_top">Using Timers in Swing Applications</a>,
133  * an article in <em>The Swing Connection.</em>
134  * <p>
135  * <strong>Warning:</strong>
136  * Serialized objects of this class will not be compatible with
137  * future Swing releases. The current serialization support is
138  * appropriate for short term storage or RMI between applications running
139  * the same version of Swing.  As of 1.4, support for long term storage
140  * of all JavaBeans&trade;
141  * has been added to the <code>java.beans</code> package.
142  * Please see {@link java.beans.XMLEncoder}.
143  *
144  * @see java.util.Timer <code>java.util.Timer</code>
145  *
146  *
147  * @author Dave Moore
148  */
149 @SuppressWarnings("serial")
150 public class Timer implements Serializable
151 {
152     /*
153      * NOTE: all fields need to be handled in readResolve
154      */
155 
156     protected EventListenerList listenerList = new EventListenerList();
157 
158     // The following field strives to maintain the following:
159     //    If coalesce is true, only allow one Runnable to be queued on the
160     //    EventQueue and be pending (ie in the process of notifying the
161     //    ActionListener). If we didn't do this it would allow for a
162     //    situation where the app is taking too long to process the
163     //    actionPerformed, and thus we'ld end up queing a bunch of Runnables
164     //    and the app would never return: not good. This of course implies
165     //    you can get dropped events, but such is life.
166     // notify is used to indicate if the ActionListener can be notified, when
167     // the Runnable is processed if this is true it will notify the listeners.
168     // notify is set to true when the Timer fires and the Runnable is queued.
169     // It will be set to false after notifying the listeners (if coalesce is
170     // true) or if the developer invokes stop.
171     private transient final AtomicBoolean notify = new AtomicBoolean(false);
172 
173     private volatile int     initialDelay, delay;
174     private volatile boolean repeats = true, coalesce = true;
175 
176     private transient final Runnable doPostEvent;
177 
178     private static volatile boolean logTimers;
179 
180     private transient final Lock lock = new ReentrantLock();
181 
182     // This field is maintained by TimerQueue.
183     // eventQueued can also be reset by the TimerQueue, but will only ever
184     // happen in applet case when TimerQueues thread is destroyed.
185     // access to this field is synchronized on getLock() lock.
186     transient TimerQueue.DelayedTimer delayedTimer = null;
187 
188     private volatile String actionCommand;
189 
190     /**
191      * Creates a {@code Timer} and initializes both the initial delay and
192      * between-event delay to {@code delay} milliseconds. If {@code delay}
193      * is less than or equal to zero, the timer fires as soon as it
194      * is started. If <code>listener</code> is not <code>null</code>,
195      * it's registered as an action listener on the timer.
196      *
197      * @param delay milliseconds for the initial and between-event delay
198      * @param listener  an initial listener; can be <code>null</code>
199      *
200      * @see #addActionListener
201      * @see #setInitialDelay
202      * @see #setRepeats
203      */
204     public Timer(int delay, ActionListener listener) {
205         super();
206         this.delay = delay;
207         this.initialDelay = delay;
208 
209         doPostEvent = new DoPostEvent();
210 
211         if (listener != null) {
212             addActionListener(listener);
213         }
214     }
215 
216     /*
217      * The timer's AccessControlContext.
218      */
219      private transient volatile AccessControlContext acc =
220             AccessController.getContext();
221 
222     /**
223       * Returns the acc this timer was constructed with.
224       */
225      final AccessControlContext getAccessControlContext() {
226        if (acc == null) {
227            throw new SecurityException(
228                    "Timer is missing AccessControlContext");
229        }
230        return acc;
231      }
232 
233     /**
234      * DoPostEvent is a runnable class that fires actionEvents to
235      * the listeners on the EventDispatchThread, via invokeLater.
236      * @see Timer#post
237      */
238     class DoPostEvent implements Runnable
239     {
240         public void run() {
241             if (logTimers) {
242                 System.out.println("Timer ringing: " + Timer.this);
243             }
244             if(notify.get()) {
245                 fireActionPerformed(new ActionEvent(Timer.this, 0, getActionCommand(),
246                                                     System.currentTimeMillis(),
247                                                     0));
248                 if (coalesce) {
249                     cancelEvent();
250                 }
251             }
252         }
253 
254         Timer getTimer() {
255             return Timer.this;
256         }
257     }
258 
259     /**
260      * Adds an action listener to the <code>Timer</code>.
261      *
262      * @param listener the listener to add
263      *
264      * @see #Timer
265      */
266     public void addActionListener(ActionListener listener) {
267         listenerList.add(ActionListener.class, listener);
268     }
269 
270 
271     /**
272      * Removes the specified action listener from the <code>Timer</code>.
273      *
274      * @param listener the listener to remove
275      */
276     public void removeActionListener(ActionListener listener) {
277         listenerList.remove(ActionListener.class, listener);
278     }
279 
280 
281     /**
282      * Returns an array of all the action listeners registered
283      * on this timer.
284      *
285      * @return all of the timer's <code>ActionListener</code>s or an empty
286      *         array if no action listeners are currently registered
287      *
288      * @see #addActionListener
289      * @see #removeActionListener
290      *
291      * @since 1.4
292      */
293     public ActionListener[] getActionListeners() {
294         return listenerList.getListeners(ActionListener.class);
295     }
296 
297 
298     /**
299      * Notifies all listeners that have registered interest for
300      * notification on this event type.
301      *
302      * @param e the action event to fire
303      * @see EventListenerList
304      */
305     protected void fireActionPerformed(ActionEvent e) {
306         // Guaranteed to return a non-null array
307         Object[] listeners = listenerList.getListenerList();
308 
309         // Process the listeners last to first, notifying
310         // those that are interested in this event
311         for (int i=listeners.length-2; i>=0; i-=2) {
312             if (listeners[i]==ActionListener.class) {
313                 ((ActionListener)listeners[i+1]).actionPerformed(e);
314             }
315         }
316     }
317 
318     /**
319      * Returns an array of all the objects currently registered as
320      * <code><em>Foo</em>Listener</code>s
321      * upon this <code>Timer</code>.
322      * <code><em>Foo</em>Listener</code>s
323      * are registered using the <code>add<em>Foo</em>Listener</code> method.
324      * <p>
325      * You can specify the <code>listenerType</code> argument
326      * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
327      * For example, you can query a <code>Timer</code>
328      * instance <code>t</code>
329      * for its action listeners
330      * with the following code:
331      *
332      * <pre>ActionListener[] als = (ActionListener[])(t.getListeners(ActionListener.class));</pre>
333      *
334      * If no such listeners exist,
335      * this method returns an empty array.
336      *
337      * @param listenerType  the type of listeners requested;
338      *          this parameter should specify an interface
339      *          that descends from <code>java.util.EventListener</code>
340      * @return an array of all objects registered as
341      *          <code><em>Foo</em>Listener</code>s
342      *          on this timer,
343      *          or an empty array if no such
344      *          listeners have been added
345      * @exception ClassCastException if <code>listenerType</code> doesn't
346      *          specify a class or interface that implements
347      *          <code>java.util.EventListener</code>
348      *
349      * @see #getActionListeners
350      * @see #addActionListener
351      * @see #removeActionListener
352      *
353      * @since 1.3
354      */
355     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
356         return listenerList.getListeners(listenerType);
357     }
358 
359     /**
360      * Returns the timer queue.
361      */
362     private TimerQueue timerQueue() {
363         return TimerQueue.sharedInstance();
364     }
365 
366 
367     /**
368      * Enables or disables the timer log. When enabled, a message
369      * is posted to <code>System.out</code> whenever the timer goes off.
370      *
371      * @param flag  <code>true</code> to enable logging
372      * @see #getLogTimers
373      */
374     public static void setLogTimers(boolean flag) {
375         logTimers = flag;
376     }
377 
378 
379     /**
380      * Returns <code>true</code> if logging is enabled.
381      *
382      * @return <code>true</code> if logging is enabled; otherwise, false
383      * @see #setLogTimers
384      */
385     public static boolean getLogTimers() {
386         return logTimers;
387     }
388 
389 
390     /**
391      * Sets the <code>Timer</code>'s between-event delay, the number of milliseconds
392      * between successive action events. This does not affect the initial delay
393      * property, which can be set by the {@code setInitialDelay} method.
394      *
395      * @param delay the delay in milliseconds
396      * @see #setInitialDelay
397      */
398     public void setDelay(int delay) {
399         if (delay < 0) {
400             throw new IllegalArgumentException("Invalid delay: " + delay);
401         }
402         else {
403             this.delay = delay;
404         }
405     }
406 
407 
408     /**
409      * Returns the delay, in milliseconds,
410      * between firings of action events.
411      *
412      * @see #setDelay
413      * @see #getInitialDelay
414      */
415     public int getDelay() {
416         return delay;
417     }
418 
419 
420     /**
421      * Sets the <code>Timer</code>'s initial delay, the time
422      * in milliseconds to wait after the timer is started
423      * before firing the first event. Upon construction, this
424      * is set to be the same as the between-event delay,
425      * but then its value is independent and remains unaffected
426      * by changes to the between-event delay.
427      *
428      * @param initialDelay the initial delay, in milliseconds
429      * @see #setDelay
430      */
431     public void setInitialDelay(int initialDelay) {
432         if (initialDelay < 0) {
433             throw new IllegalArgumentException("Invalid initial delay: " +
434                                                initialDelay);
435         }
436         else {
437             this.initialDelay = initialDelay;
438         }
439     }
440 
441 
442     /**
443      * Returns the <code>Timer</code>'s initial delay.
444      *
445      * @see #setInitialDelay
446      * @see #setDelay
447      */
448     public int getInitialDelay() {
449         return initialDelay;
450     }
451 
452 
453     /**
454      * If <code>flag</code> is <code>false</code>,
455      * instructs the <code>Timer</code> to send only one
456      * action event to its listeners.
457      *
458      * @param flag specify <code>false</code> to make the timer
459      *             stop after sending its first action event
460      */
461     public void setRepeats(boolean flag) {
462         repeats = flag;
463     }
464 
465 
466     /**
467      * Returns <code>true</code> (the default)
468      * if the <code>Timer</code> will send
469      * an action event
470      * to its listeners multiple times.
471      *
472      * @see #setRepeats
473      */
474     public boolean isRepeats() {
475         return repeats;
476     }
477 
478 
479     /**
480      * Sets whether the <code>Timer</code> coalesces multiple pending
481      * <code>ActionEvent</code> firings.
482      * A busy application may not be able
483      * to keep up with a <code>Timer</code>'s event generation,
484      * causing multiple
485      * action events to be queued.  When processed,
486      * the application sends these events one after the other, causing the
487      * <code>Timer</code>'s listeners to receive a sequence of
488      * events with no delay between them. Coalescing avoids this situation
489      * by reducing multiple pending events to a single event.
490      * <code>Timer</code>s
491      * coalesce events by default.
492      *
493      * @param flag specify <code>false</code> to turn off coalescing
494      */
495     public void setCoalesce(boolean flag) {
496         boolean old = coalesce;
497         coalesce = flag;
498         if (!old && coalesce) {
499             // We must do this as otherwise if the Timer once notified
500             // in !coalese mode notify will be stuck to true and never
501             // become false.
502             cancelEvent();
503         }
504     }
505 
506 
507     /**
508      * Returns <code>true</code> if the <code>Timer</code> coalesces
509      * multiple pending action events.
510      *
511      * @see #setCoalesce
512      */
513     public boolean isCoalesce() {
514         return coalesce;
515     }
516 
517 
518     /**
519      * Sets the string that will be delivered as the action command
520      * in <code>ActionEvent</code>s fired by this timer.
521      * <code>null</code> is an acceptable value.
522      *
523      * @param command the action command
524      * @since 1.6
525      */
526     public void setActionCommand(String command) {
527         this.actionCommand = command;
528     }
529 
530 
531     /**
532      * Returns the string that will be delivered as the action command
533      * in <code>ActionEvent</code>s fired by this timer. May be
534      * <code>null</code>, which is also the default.
535      *
536      * @return the action command used in firing events
537      * @since 1.6
538      */
539     public String getActionCommand() {
540         return actionCommand;
541     }
542 
543 
544     /**
545      * Starts the <code>Timer</code>,
546      * causing it to start sending action events
547      * to its listeners.
548      *
549      * @see #stop
550      */
551      public void start() {
552         timerQueue().addTimer(this, getInitialDelay());
553     }
554 
555 
556     /**
557      * Returns <code>true</code> if the <code>Timer</code> is running.
558      *
559      * @see #start
560      */
561     public boolean isRunning() {
562         return timerQueue().containsTimer(this);
563     }
564 
565 
566     /**
567      * Stops the <code>Timer</code>,
568      * causing it to stop sending action events
569      * to its listeners.
570      *
571      * @see #start
572      */
573     public void stop() {
574         getLock().lock();
575         try {
576             cancelEvent();
577             timerQueue().removeTimer(this);
578         } finally {
579             getLock().unlock();
580         }
581     }
582 
583 
584     /**
585      * Restarts the <code>Timer</code>,
586      * canceling any pending firings and causing
587      * it to fire with its initial delay.
588      */
589     public void restart() {
590         getLock().lock();
591         try {
592             stop();
593             start();
594         } finally {
595             getLock().unlock();
596         }
597     }
598 
599 
600     /**
601      * Resets the internal state to indicate this Timer shouldn't notify
602      * any of its listeners. This does not stop a repeatable Timer from
603      * firing again, use <code>stop</code> for that.
604      */
605     void cancelEvent() {
606         notify.set(false);
607     }
608 
609 
610     void post() {
611          if (notify.compareAndSet(false, true) || !coalesce) {
612              AccessController.doPrivileged(new PrivilegedAction<Void>() {
613                  public Void run() {
614                      SwingUtilities.invokeLater(doPostEvent);
615                      return null;
616                 }
617             }, getAccessControlContext());
618         }
619     }
620 
621     Lock getLock() {
622         return lock;
623     }
624 
625     private void readObject(ObjectInputStream in)
626         throws ClassNotFoundException, IOException
627     {
628         this.acc = AccessController.getContext();
629         in.defaultReadObject();
630     }
631 
632     /*
633      * We have to use readResolve because we can not initialize final
634      * fields for deserialized object otherwise
635      */
636     private Object readResolve() {
637         Timer timer = new Timer(getDelay(), null);
638         timer.listenerList = listenerList;
639         timer.initialDelay = initialDelay;
640         timer.delay = delay;
641         timer.repeats = repeats;
642         timer.coalesce = coalesce;
643         timer.actionCommand = actionCommand;
644         return timer;
645     }
646 }