1 /*
2  * Copyright (c) 1999, 2017, 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 package javax.management.timer;
27 
28 import static com.sun.jmx.defaults.JmxProperties.TIMER_LOGGER;
29 import java.util.ArrayList;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.TreeSet;
35 import java.util.Vector;
36 import java.lang.System.Logger.Level;
37 
38 // jmx imports
39 //
40 import javax.management.InstanceNotFoundException;
41 import javax.management.MBeanNotificationInfo;
42 import javax.management.MBeanRegistration;
43 import javax.management.MBeanServer;
44 import javax.management.NotificationBroadcasterSupport;
45 import javax.management.ObjectName;
46 
47 /**
48  *
49  * Provides the implementation of the timer MBean.
50  * The timer MBean sends out an alarm at a specified time
51  * that wakes up all the listeners registered to receive timer notifications.
52  * <P>
53  *
54  * This class manages a list of dated timer notifications.
55  * A method allows users to add/remove as many notifications as required.
56  * When a timer notification is emitted by the timer and becomes obsolete,
57  * it is automatically removed from the list of timer notifications.
58  * <BR>Additional timer notifications can be added into regularly repeating notifications.
59  * <P>
60  *
61  * Note:
62  * <OL>
63  * <LI>When sending timer notifications, the timer updates the notification sequence number
64  * irrespective of the notification type.
65  * <LI>The timer service relies on the system date of the host where the <CODE>Timer</CODE> class is loaded.
66  * Listeners may receive untimely notifications
67  * if their host has a different system date.
68  * To avoid such problems, synchronize the system date of all host machines where timing is needed.
69  * <LI>The default behavior for periodic notifications is <i>fixed-delay execution</i>, as
70  *     specified in {@link java.util.Timer}. In order to use <i>fixed-rate execution</i>, use the
71  *     overloaded {@link #addNotification(String, String, Object, Date, long, long, boolean)} method.
72  * <LI>Notification listeners are potentially all executed in the same
73  * thread.  Therefore, they should execute rapidly to avoid holding up
74  * other listeners or perturbing the regularity of fixed-delay
75  * executions.  See {@link NotificationBroadcasterSupport}.
76  * </OL>
77  *
78  * @since 1.5
79  */
80 public class Timer extends NotificationBroadcasterSupport
81         implements TimerMBean, MBeanRegistration {
82 
83 
84     /*
85      * ------------------------------------------
86      *  PUBLIC VARIABLES
87      * ------------------------------------------
88      */
89 
90     /**
91      * Number of milliseconds in one second.
92      * Useful constant for the <CODE>addNotification</CODE> method.
93      */
94     public static final long ONE_SECOND = 1000;
95 
96     /**
97      * Number of milliseconds in one minute.
98      * Useful constant for the <CODE>addNotification</CODE> method.
99      */
100     public static final long ONE_MINUTE = 60*ONE_SECOND;
101 
102     /**
103      * Number of milliseconds in one hour.
104      * Useful constant for the <CODE>addNotification</CODE> method.
105      */
106     public static final long ONE_HOUR   = 60*ONE_MINUTE;
107 
108     /**
109      * Number of milliseconds in one day.
110      * Useful constant for the <CODE>addNotification</CODE> method.
111      */
112     public static final long ONE_DAY    = 24*ONE_HOUR;
113 
114     /**
115      * Number of milliseconds in one week.
116      * Useful constant for the <CODE>addNotification</CODE> method.
117      */
118     public static final long ONE_WEEK   = 7*ONE_DAY;
119 
120     /*
121      * ------------------------------------------
122      *  PRIVATE VARIABLES
123      * ------------------------------------------
124      */
125 
126     /**
127      * Table containing all the timer notifications of this timer,
128      * with the associated date, period and number of occurrences.
129      */
130     final private Map<Integer,Object[]> timerTable =
131         new HashMap<>();
132 
133     /**
134      * Past notifications sending on/off flag value.
135      * This attribute is used to specify if the timer has to send past notifications after start.
136      * <BR>The default value is set to <CODE>false</CODE>.
137      */
138     private boolean sendPastNotifications = false;
139 
140     /**
141      * Timer state.
142      * The default value is set to <CODE>false</CODE>.
143      */
144     private transient boolean isActive = false;
145 
146     /**
147      * Timer sequence number.
148      * The default value is set to 0.
149      */
150     private transient long sequenceNumber = 0;
151 
152     // Flags needed to keep the indexes of the objects in the array.
153     //
154     private static final int TIMER_NOTIF_INDEX     = 0;
155     private static final int TIMER_DATE_INDEX      = 1;
156     private static final int TIMER_PERIOD_INDEX    = 2;
157     private static final int TIMER_NB_OCCUR_INDEX  = 3;
158     private static final int ALARM_CLOCK_INDEX     = 4;
159     private static final int FIXED_RATE_INDEX      = 5;
160 
161     /**
162      * The notification counter ID.
163      * Used to keep the max key value inserted into the timer table.
164      */
165     volatile private int counterID = 0;
166 
167     private java.util.Timer timer;
168 
169     /*
170      * ------------------------------------------
171      *  CONSTRUCTORS
172      * ------------------------------------------
173      */
174 
175     /**
176      * Default constructor.
177      */
Timer()178     public Timer() {
179     }
180 
181     /*
182      * ------------------------------------------
183      *  PUBLIC METHODS
184      * ------------------------------------------
185      */
186 
187     /**
188      * Allows the timer MBean to perform any operations it needs before being registered
189      * in the MBean server.
190      * <P>
191      * Not used in this context.
192      *
193      * @param server The MBean server in which the timer MBean will be registered.
194      * @param name The object name of the timer MBean.
195      *
196      * @return The name of the timer MBean registered.
197      *
198      * @exception java.lang.Exception if something goes wrong
199      */
preRegister(MBeanServer server, ObjectName name)200     public ObjectName preRegister(MBeanServer server, ObjectName name)
201         throws java.lang.Exception {
202         return name;
203     }
204 
205     /**
206      * Allows the timer MBean to perform any operations needed after having been
207      * registered in the MBean server or after the registration has failed.
208      * <P>
209      * Not used in this context.
210      */
postRegister(Boolean registrationDone)211     public void postRegister (Boolean registrationDone) {
212     }
213 
214     /**
215      * Allows the timer MBean to perform any operations it needs before being unregistered
216      * by the MBean server.
217      * <P>
218      * Stops the timer.
219      *
220      * @exception java.lang.Exception if something goes wrong
221      */
preDeregister()222     public void preDeregister() throws java.lang.Exception {
223 
224         TIMER_LOGGER.log(Level.TRACE, "stop the timer");
225 
226         // Stop the timer.
227         //
228         stop();
229     }
230 
231     /**
232      * Allows the timer MBean to perform any operations needed after having been
233      * unregistered by the MBean server.
234      * <P>
235      * Not used in this context.
236      */
postDeregister()237     public void postDeregister() {
238     }
239 
240     /*
241      * This overrides the method in NotificationBroadcasterSupport.
242      * Return the MBeanNotificationInfo[] array for this MBean.
243      * The returned array has one element to indicate that the MBean
244      * can emit TimerNotification.  The array of type strings
245      * associated with this entry is a snapshot of the current types
246      * that were given to addNotification.
247      */
getNotificationInfo()248     public synchronized MBeanNotificationInfo[] getNotificationInfo() {
249         Set<String> notifTypes = new TreeSet<String>();
250         for (Object[] entry : timerTable.values()) {
251             TimerNotification notif = (TimerNotification)
252                 entry[TIMER_NOTIF_INDEX];
253             notifTypes.add(notif.getType());
254         }
255         String[] notifTypesArray =
256             notifTypes.toArray(new String[0]);
257         return new MBeanNotificationInfo[] {
258             new MBeanNotificationInfo(notifTypesArray,
259                                       TimerNotification.class.getName(),
260                                       "Notification sent by Timer MBean")
261         };
262     }
263 
264     /**
265      * Starts the timer.
266      * <P>
267      * If there is one or more timer notifications before the time in the list of notifications, the notification
268      * is sent according to the <CODE>sendPastNotifications</CODE> flag and then, updated
269      * according to its period and remaining number of occurrences.
270      * If the timer notification date remains earlier than the current date, this notification is just removed
271      * from the list of notifications.
272      */
start()273     public synchronized void start() {
274 
275         TIMER_LOGGER.log(Level.TRACE, "starting the timer");
276 
277         // Start the TimerAlarmClock.
278         //
279         if (isActive == false) {
280 
281             timer = new java.util.Timer();
282 
283             TimerAlarmClock alarmClock;
284             Date date;
285 
286             Date currentDate = new Date();
287 
288             // Send or not past notifications depending on the flag.
289             // Update the date and the number of occurrences of past notifications
290             // to make them later than the current date.
291             //
292             sendPastNotifications(currentDate, sendPastNotifications);
293 
294             // Update and start all the TimerAlarmClocks.
295             // Here, all the notifications in the timer table are later than the current date.
296             //
297             for (Object[] obj : timerTable.values()) {
298 
299                 // Retrieve the date notification and the TimerAlarmClock.
300                 //
301                 date = (Date)obj[TIMER_DATE_INDEX];
302 
303                 // Update all the TimerAlarmClock timeouts and start them.
304                 //
305                 boolean fixedRate = ((Boolean)obj[FIXED_RATE_INDEX]).booleanValue();
306                 if (fixedRate)
307                 {
308                   alarmClock = new TimerAlarmClock(this, date);
309                   obj[ALARM_CLOCK_INDEX] = (Object)alarmClock;
310                   timer.schedule(alarmClock, alarmClock.next);
311                 }
312                 else
313                 {
314                   alarmClock = new TimerAlarmClock(this, (date.getTime() - currentDate.getTime()));
315                   obj[ALARM_CLOCK_INDEX] = (Object)alarmClock;
316                   timer.schedule(alarmClock, alarmClock.timeout);
317                 }
318             }
319 
320             // Set the state to ON.
321             //
322             isActive = true;
323 
324             TIMER_LOGGER.log(Level.TRACE, "timer started");
325         } else {
326             TIMER_LOGGER.log(Level.TRACE, "the timer is already activated");
327         }
328     }
329 
330     /**
331      * Stops the timer.
332      */
stop()333     public synchronized void stop() {
334 
335         TIMER_LOGGER.log(Level.TRACE, "stopping the timer");
336 
337         // Stop the TimerAlarmClock.
338         //
339         if (isActive == true) {
340 
341             for (Object[] obj : timerTable.values()) {
342 
343                 // Stop all the TimerAlarmClock.
344                 //
345                 TimerAlarmClock alarmClock = (TimerAlarmClock)obj[ALARM_CLOCK_INDEX];
346                 if (alarmClock != null) {
347 //                     alarmClock.interrupt();
348 //                     try {
349 //                         // Wait until the thread die.
350 //                         //
351 //                         alarmClock.join();
352 //                     } catch (InterruptedException ex) {
353 //                         // Ignore...
354 //                     }
355 //                     // Remove the reference on the TimerAlarmClock.
356 //                     //
357 
358                     alarmClock.cancel();
359                 }
360             }
361 
362             timer.cancel();
363 
364             // Set the state to OFF.
365             //
366             isActive = false;
367 
368             TIMER_LOGGER.log(Level.TRACE, "timer stopped");
369         } else {
370             TIMER_LOGGER.log(Level.TRACE, "the timer is already deactivated");
371         }
372     }
373 
374     /**
375      * Creates a new timer notification with the specified <CODE>type</CODE>, <CODE>message</CODE>
376      * and <CODE>userData</CODE> and inserts it into the list of notifications with a given date,
377      * period and number of occurrences.
378      * <P>
379      * If the timer notification to be inserted has a date that is before the current date,
380      * the method behaves as if the specified date were the current date. <BR>
381      * For once-off notifications, the notification is delivered immediately. <BR>
382      * For periodic notifications, the first notification is delivered immediately and the
383      * subsequent ones are spaced as specified by the period parameter.
384      * <P>
385      * Note that once the timer notification has been added into the list of notifications,
386      * its associated date, period and number of occurrences cannot be updated.
387      * <P>
388      * In the case of a periodic notification, the value of parameter <i>fixedRate</i> is used to
389      * specify the execution scheme, as specified in {@link java.util.Timer}.
390      *
391      * @param type The timer notification type.
392      * @param message The timer notification detailed message.
393      * @param userData The timer notification user data object.
394      * @param date The date when the notification occurs.
395      * @param period The period of the timer notification (in milliseconds).
396      * @param nbOccurences The total number the timer notification will be emitted.
397      * @param fixedRate If <code>true</code> and if the notification is periodic, the notification
398      *                  is scheduled with a <i>fixed-rate</i> execution scheme. If
399      *                  <code>false</code> and if the notification is periodic, the notification
400      *                  is scheduled with a <i>fixed-delay</i> execution scheme. Ignored if the
401      *                  notification is not periodic.
402      *
403      * @return The identifier of the new created timer notification.
404      *
405      * @exception java.lang.IllegalArgumentException The date is {@code null} or
406      * the period or the number of occurrences is negative.
407      *
408      * @see #addNotification(String, String, Object, Date, long, long)
409      */
410 // NPCTE fix for bugId 4464388, esc 0,  MR, to be added after modification of jmx spec
411 //  public synchronized Integer addNotification(String type, String message, Serializable userData,
412 //                                                Date date, long period, long nbOccurences)
413 // end of NPCTE fix for bugId 4464388
414 
addNotification(String type, String message, Object userData, Date date, long period, long nbOccurences, boolean fixedRate)415     public synchronized Integer addNotification(String type, String message, Object userData,
416                                                 Date date, long period, long nbOccurences, boolean fixedRate)
417         throws java.lang.IllegalArgumentException {
418 
419         if (date == null) {
420             throw new java.lang.IllegalArgumentException("Timer notification date cannot be null.");
421         }
422 
423         // Check that all the timer notification attributes are valid.
424         //
425 
426         // Invalid timer period value exception:
427         // Check that the period and the nbOccurences are POSITIVE VALUES.
428         //
429         if ((period < 0) || (nbOccurences < 0)) {
430             throw new java.lang.IllegalArgumentException("Negative values for the periodicity");
431         }
432 
433         Date currentDate = new Date();
434 
435         // Update the date if it is before the current date.
436         //
437         if (currentDate.after(date)) {
438 
439             date.setTime(currentDate.getTime());
440             if (TIMER_LOGGER.isLoggable(Level.TRACE)) {
441                 TIMER_LOGGER.log(Level.TRACE,
442                         "update timer notification to add with:" +
443                         "\n\tNotification date = " + date);
444             }
445         }
446 
447         // Create and add the timer notification into the timer table.
448         //
449         Integer notifID = Integer.valueOf(++counterID);
450 
451         // The sequenceNumber and the timeStamp attributes are updated
452         // when the notification is emitted by the timer.
453         //
454         TimerNotification notif = new TimerNotification(type, this, 0, 0, message, notifID);
455         notif.setUserData(userData);
456 
457         Object[] obj = new Object[6];
458 
459         TimerAlarmClock alarmClock;
460         if (fixedRate)
461         {
462           alarmClock = new TimerAlarmClock(this, date);
463         }
464         else
465         {
466           alarmClock = new TimerAlarmClock(this, (date.getTime() - currentDate.getTime()));
467         }
468 
469         // Fix bug 00417.B
470         // The date registered into the timer is a clone from the date parameter.
471         //
472         Date d = new Date(date.getTime());
473 
474         obj[TIMER_NOTIF_INDEX] = (Object)notif;
475         obj[TIMER_DATE_INDEX] = (Object)d;
476         obj[TIMER_PERIOD_INDEX] = (Object) period;
477         obj[TIMER_NB_OCCUR_INDEX] = (Object) nbOccurences;
478         obj[ALARM_CLOCK_INDEX] = (Object)alarmClock;
479         obj[FIXED_RATE_INDEX] = Boolean.valueOf(fixedRate);
480 
481         if (TIMER_LOGGER.isLoggable(Level.TRACE)) {
482             StringBuilder strb = new StringBuilder()
483             .append("adding timer notification:\n\t")
484             .append("Notification source = ")
485             .append(notif.getSource())
486             .append("\n\tNotification type = ")
487             .append(notif.getType())
488             .append("\n\tNotification ID = ")
489             .append(notifID)
490             .append("\n\tNotification date = ")
491             .append(d)
492             .append("\n\tNotification period = ")
493             .append(period)
494             .append("\n\tNotification nb of occurrences = ")
495             .append(nbOccurences)
496             .append("\n\tNotification executes at fixed rate = ")
497             .append(fixedRate);
498             TIMER_LOGGER.log(Level.TRACE, strb::toString);
499         }
500 
501         timerTable.put(notifID, obj);
502 
503         // Update and start the TimerAlarmClock.
504         //
505         if (isActive == true) {
506           if (fixedRate)
507           {
508             timer.schedule(alarmClock, alarmClock.next);
509           }
510           else
511           {
512             timer.schedule(alarmClock, alarmClock.timeout);
513           }
514         }
515 
516         TIMER_LOGGER.log(Level.TRACE, "timer notification added");
517         return notifID;
518     }
519 
520     /**
521      * Creates a new timer notification with the specified <CODE>type</CODE>, <CODE>message</CODE>
522      * and <CODE>userData</CODE> and inserts it into the list of notifications with a given date,
523      * period and number of occurrences.
524      * <P>
525      * If the timer notification to be inserted has a date that is before the current date,
526      * the method behaves as if the specified date were the current date. <BR>
527      * For once-off notifications, the notification is delivered immediately. <BR>
528      * For periodic notifications, the first notification is delivered immediately and the
529      * subsequent ones are spaced as specified by the period parameter.
530      * <P>
531      * Note that once the timer notification has been added into the list of notifications,
532      * its associated date, period and number of occurrences cannot be updated.
533      * <P>
534      * In the case of a periodic notification, uses a <i>fixed-delay</i> execution scheme, as specified in
535      * {@link java.util.Timer}. In order to use a <i>fixed-rate</i> execution scheme, use
536      * {@link #addNotification(String, String, Object, Date, long, long, boolean)} instead.
537      *
538      * @param type The timer notification type.
539      * @param message The timer notification detailed message.
540      * @param userData The timer notification user data object.
541      * @param date The date when the notification occurs.
542      * @param period The period of the timer notification (in milliseconds).
543      * @param nbOccurences The total number the timer notification will be emitted.
544      *
545      * @return The identifier of the new created timer notification.
546      *
547      * @exception java.lang.IllegalArgumentException The date is {@code null} or
548      * the period or the number of occurrences is negative.
549      *
550      * @see #addNotification(String, String, Object, Date, long, long, boolean)
551      */
552 // NPCTE fix for bugId 4464388, esc 0,  MR , to be added after modification of jmx spec
553 //  public synchronized Integer addNotification(String type, String message, Serializable userData,
554 //                                              Date date, long period)
555 // end of NPCTE fix for bugId 4464388 */
556 
addNotification(String type, String message, Object userData, Date date, long period, long nbOccurences)557     public synchronized Integer addNotification(String type, String message, Object userData,
558                                                 Date date, long period, long nbOccurences)
559         throws java.lang.IllegalArgumentException {
560 
561       return addNotification(type, message, userData, date, period, nbOccurences, false);
562     }
563 
564     /**
565      * Creates a new timer notification with the specified <CODE>type</CODE>, <CODE>message</CODE>
566      * and <CODE>userData</CODE> and inserts it into the list of notifications with a given date
567      * and period and a null number of occurrences.
568      * <P>
569      * The timer notification will repeat continuously using the timer period using a <i>fixed-delay</i>
570      * execution scheme, as specified in {@link java.util.Timer}. In order to use a <i>fixed-rate</i>
571      * execution scheme, use {@link #addNotification(String, String, Object, Date, long, long,
572      * boolean)} instead.
573      * <P>
574      * If the timer notification to be inserted has a date that is before the current date,
575      * the method behaves as if the specified date were the current date. The
576      * first notification is delivered immediately and the subsequent ones are
577      * spaced as specified by the period parameter.
578      *
579      * @param type The timer notification type.
580      * @param message The timer notification detailed message.
581      * @param userData The timer notification user data object.
582      * @param date The date when the notification occurs.
583      * @param period The period of the timer notification (in milliseconds).
584      *
585      * @return The identifier of the new created timer notification.
586      *
587      * @exception java.lang.IllegalArgumentException The date is {@code null} or
588      * the period is negative.
589      */
590 // NPCTE fix for bugId 4464388, esc 0,  MR , to be added after modification of jmx spec
591 //  public synchronized Integer addNotification(String type, String message, Serializable userData,
592 //                                              Date date, long period)
593 // end of NPCTE fix for bugId 4464388 */
594 
addNotification(String type, String message, Object userData, Date date, long period)595     public synchronized Integer addNotification(String type, String message, Object userData,
596                                                 Date date, long period)
597         throws java.lang.IllegalArgumentException {
598 
599         return (addNotification(type, message, userData, date, period, 0));
600     }
601 
602     /**
603      * Creates a new timer notification with the specified <CODE>type</CODE>, <CODE>message</CODE>
604      * and <CODE>userData</CODE> and inserts it into the list of notifications with a given date
605      * and a null period and number of occurrences.
606      * <P>
607      * The timer notification will be handled once at the specified date.
608      * <P>
609      * If the timer notification to be inserted has a date that is before the current date,
610      * the method behaves as if the specified date were the current date and the
611      * notification is delivered immediately.
612      *
613      * @param type The timer notification type.
614      * @param message The timer notification detailed message.
615      * @param userData The timer notification user data object.
616      * @param date The date when the notification occurs.
617      *
618      * @return The identifier of the new created timer notification.
619      *
620      * @exception java.lang.IllegalArgumentException The date is {@code null}.
621      */
622 // NPCTE fix for bugId 4464388, esc 0,  MR, to be added after modification of jmx spec
623 //  public synchronized Integer addNotification(String type, String message, Serializable userData, Date date)
624 //      throws java.lang.IllegalArgumentException {
625 // end of NPCTE fix for bugId 4464388
626 
addNotification(String type, String message, Object userData, Date date)627     public synchronized Integer addNotification(String type, String message, Object userData, Date date)
628         throws java.lang.IllegalArgumentException {
629 
630 
631         return (addNotification(type, message, userData, date, 0, 0));
632     }
633 
634     /**
635      * Removes the timer notification corresponding to the specified identifier from the list of notifications.
636      *
637      * @param id The timer notification identifier.
638      *
639      * @exception InstanceNotFoundException The specified identifier does not correspond to any timer notification
640      * in the list of notifications of this timer MBean.
641      */
removeNotification(Integer id)642     public synchronized void removeNotification(Integer id) throws InstanceNotFoundException {
643 
644         // Check that the notification to remove is effectively in the timer table.
645         //
646         if (timerTable.containsKey(id) == false) {
647             throw new InstanceNotFoundException("Timer notification to remove not in the list of notifications");
648         }
649 
650         // Stop the TimerAlarmClock.
651         //
652         Object[] obj = timerTable.get(id);
653         TimerAlarmClock alarmClock = (TimerAlarmClock)obj[ALARM_CLOCK_INDEX];
654         if (alarmClock != null) {
655 //             alarmClock.interrupt();
656 //             try {
657 //                 // Wait until the thread die.
658 //                 //
659 //                 alarmClock.join();
660 //             } catch (InterruptedException e) {
661 //                 // Ignore...
662 //             }
663 //             // Remove the reference on the TimerAlarmClock.
664 //             //
665             alarmClock.cancel();
666         }
667 
668         // Remove the timer notification from the timer table.
669         //
670         if (TIMER_LOGGER.isLoggable(Level.TRACE)) {
671             StringBuilder strb = new StringBuilder()
672             .append("removing timer notification:")
673             .append("\n\tNotification source = ")
674             .append(((TimerNotification)obj[TIMER_NOTIF_INDEX]).getSource())
675             .append("\n\tNotification type = ")
676             .append(((TimerNotification)obj[TIMER_NOTIF_INDEX]).getType())
677             .append("\n\tNotification ID = ")
678             .append(((TimerNotification)obj[TIMER_NOTIF_INDEX]).getNotificationID())
679             .append("\n\tNotification date = ")
680             .append(obj[TIMER_DATE_INDEX])
681             .append("\n\tNotification period = ")
682             .append(obj[TIMER_PERIOD_INDEX])
683             .append("\n\tNotification nb of occurrences = ")
684             .append(obj[TIMER_NB_OCCUR_INDEX])
685             .append("\n\tNotification executes at fixed rate = ")
686             .append(obj[FIXED_RATE_INDEX]);
687             TIMER_LOGGER.log(Level.TRACE, strb::toString);
688         }
689 
690         timerTable.remove(id);
691 
692         TIMER_LOGGER.log(Level.TRACE, "timer notification removed");
693     }
694 
695     /**
696      * Removes all the timer notifications corresponding to the specified type from the list of notifications.
697      *
698      * @param type The timer notification type.
699      *
700      * @exception InstanceNotFoundException The specified type does not correspond to any timer notification
701      * in the list of notifications of this timer MBean.
702      */
removeNotifications(String type)703     public synchronized void removeNotifications(String type) throws InstanceNotFoundException {
704 
705         Vector<Integer> v = getNotificationIDs(type);
706 
707         if (v.isEmpty())
708             throw new InstanceNotFoundException("Timer notifications to remove not in the list of notifications");
709 
710         for (Integer i : v)
711             removeNotification(i);
712     }
713 
714     /**
715      * Removes all the timer notifications from the list of notifications
716      * and resets the counter used to update the timer notification identifiers.
717      */
removeAllNotifications()718     public synchronized void removeAllNotifications() {
719 
720         TimerAlarmClock alarmClock;
721 
722         for (Object[] obj : timerTable.values()) {
723 
724             // Stop the TimerAlarmClock.
725             //
726             alarmClock = (TimerAlarmClock)obj[ALARM_CLOCK_INDEX];
727 //             if (alarmClock != null) {
728 //                 alarmClock.interrupt();
729 //                 try {
730 //                     // Wait until the thread die.
731 //                     //
732 //                     alarmClock.join();
733 //                 } catch (InterruptedException ex) {
734 //                     // Ignore...
735 //                 }
736                   // Remove the reference on the TimerAlarmClock.
737                   //
738 //             }
739             alarmClock.cancel();
740         }
741 
742         // Remove all the timer notifications from the timer table.
743         TIMER_LOGGER.log(Level.TRACE, "removing all timer notifications");
744 
745         timerTable.clear();
746 
747         TIMER_LOGGER.log(Level.TRACE, "all timer notifications removed");
748         // Reset the counterID.
749         //
750         counterID = 0;
751 
752         TIMER_LOGGER.log(Level.TRACE, "timer notification counter ID reset");
753     }
754 
755     // GETTERS AND SETTERS
756     //--------------------
757 
758     /**
759      * Gets the number of timer notifications registered into the list of notifications.
760      *
761      * @return The number of timer notifications.
762      */
getNbNotifications()763     public synchronized int getNbNotifications() {
764         return timerTable.size();
765     }
766 
767     /**
768      * Gets all timer notification identifiers registered into the list of notifications.
769      *
770      * @return A vector of <CODE>Integer</CODE> objects containing all the timer notification identifiers.
771      * <BR>The vector is empty if there is no timer notification registered for this timer MBean.
772      */
getAllNotificationIDs()773     public synchronized Vector<Integer> getAllNotificationIDs() {
774         return new Vector<Integer>(timerTable.keySet());
775     }
776 
777     /**
778      * Gets all the identifiers of timer notifications corresponding to the specified type.
779      *
780      * @param type The timer notification type.
781      *
782      * @return A vector of <CODE>Integer</CODE> objects containing all the identifiers of
783      * timer notifications with the specified <CODE>type</CODE>.
784      * <BR>The vector is empty if there is no timer notifications registered for this timer MBean
785      * with the specified <CODE>type</CODE>.
786      */
getNotificationIDs(String type)787     public synchronized Vector<Integer> getNotificationIDs(String type) {
788 
789         String s;
790 
791         Vector<Integer> v = new Vector<Integer>();
792 
793         for (Map.Entry<Integer,Object[]> entry : timerTable.entrySet()) {
794             Object[] obj = entry.getValue();
795             s = ((TimerNotification)obj[TIMER_NOTIF_INDEX]).getType();
796             if ((type == null) ? s == null : type.equals(s))
797                 v.addElement(entry.getKey());
798         }
799         return v;
800     }
801     // 5089997: return is Vector<Integer> not Vector<TimerNotification>
802 
803     /**
804      * Gets the timer notification type corresponding to the specified identifier.
805      *
806      * @param id The timer notification identifier.
807      *
808      * @return The timer notification type or null if the identifier is not mapped to any
809      * timer notification registered for this timer MBean.
810      */
getNotificationType(Integer id)811     public synchronized String getNotificationType(Integer id) {
812 
813         Object[] obj = timerTable.get(id);
814         if (obj != null) {
815             return ( ((TimerNotification)obj[TIMER_NOTIF_INDEX]).getType() );
816         }
817         return null;
818     }
819 
820     /**
821      * Gets the timer notification detailed message corresponding to the specified identifier.
822      *
823      * @param id The timer notification identifier.
824      *
825      * @return The timer notification detailed message or null if the identifier is not mapped to any
826      * timer notification registered for this timer MBean.
827      */
getNotificationMessage(Integer id)828     public synchronized String getNotificationMessage(Integer id) {
829 
830         Object[] obj = timerTable.get(id);
831         if (obj != null) {
832             return ( ((TimerNotification)obj[TIMER_NOTIF_INDEX]).getMessage() );
833         }
834         return null;
835     }
836 
837     /**
838      * Gets the timer notification user data object corresponding to the specified identifier.
839      *
840      * @param id The timer notification identifier.
841      *
842      * @return The timer notification user data object or null if the identifier is not mapped to any
843      * timer notification registered for this timer MBean.
844      */
845     // NPCTE fix for bugId 4464388, esc 0, MR, 03 sept 2001, to be added after modification of jmx spec
846     //public Serializable getNotificationUserData(Integer id) {
847     // end of NPCTE fix for bugId 4464388
848 
getNotificationUserData(Integer id)849     public synchronized Object getNotificationUserData(Integer id) {
850         Object[] obj = timerTable.get(id);
851         if (obj != null) {
852             return ( ((TimerNotification)obj[TIMER_NOTIF_INDEX]).getUserData() );
853         }
854         return null;
855     }
856 
857     /**
858      * Gets a copy of the date associated to a timer notification.
859      *
860      * @param id The timer notification identifier.
861      *
862      * @return A copy of the date or null if the identifier is not mapped to any
863      * timer notification registered for this timer MBean.
864      */
getDate(Integer id)865     public synchronized Date getDate(Integer id) {
866 
867         Object[] obj = timerTable.get(id);
868         if (obj != null) {
869             Date date = (Date)obj[TIMER_DATE_INDEX];
870             return (new Date(date.getTime()));
871         }
872         return null;
873     }
874 
875     /**
876      * Gets a copy of the period (in milliseconds) associated to a timer notification.
877      *
878      * @param id The timer notification identifier.
879      *
880      * @return A copy of the period or null if the identifier is not mapped to any
881      * timer notification registered for this timer MBean.
882      */
getPeriod(Integer id)883     public synchronized Long getPeriod(Integer id) {
884 
885         Object[] obj = timerTable.get(id);
886         if (obj != null) {
887             return (Long)obj[TIMER_PERIOD_INDEX];
888         }
889         return null;
890     }
891 
892     /**
893      * Gets a copy of the remaining number of occurrences associated to a timer notification.
894      *
895      * @param id The timer notification identifier.
896      *
897      * @return A copy of the remaining number of occurrences or null if the identifier is not mapped to any
898      * timer notification registered for this timer MBean.
899      */
getNbOccurences(Integer id)900     public synchronized Long getNbOccurences(Integer id) {
901 
902         Object[] obj = timerTable.get(id);
903         if (obj != null) {
904             return (Long)obj[TIMER_NB_OCCUR_INDEX];
905         }
906         return null;
907     }
908 
909     /**
910      * Gets a copy of the flag indicating whether a periodic notification is
911      * executed at <i>fixed-delay</i> or at <i>fixed-rate</i>.
912      *
913      * @param id The timer notification identifier.
914      *
915      * @return A copy of the flag indicating whether a periodic notification is
916      *         executed at <i>fixed-delay</i> or at <i>fixed-rate</i>.
917      */
getFixedRate(Integer id)918     public synchronized Boolean getFixedRate(Integer id) {
919 
920       Object[] obj = timerTable.get(id);
921       if (obj != null) {
922         Boolean fixedRate = (Boolean)obj[FIXED_RATE_INDEX];
923         return (Boolean.valueOf(fixedRate.booleanValue()));
924       }
925       return null;
926     }
927 
928     /**
929      * Gets the flag indicating whether or not the timer sends past notifications.
930      * <BR>The default value of the past notifications sending on/off flag is <CODE>false</CODE>.
931      *
932      * @return The past notifications sending on/off flag value.
933      *
934      * @see #setSendPastNotifications
935      */
getSendPastNotifications()936     public boolean getSendPastNotifications() {
937         return sendPastNotifications;
938     }
939 
940     /**
941      * Sets the flag indicating whether the timer sends past notifications or not.
942      * <BR>The default value of the past notifications sending on/off flag is <CODE>false</CODE>.
943      *
944      * @param value The past notifications sending on/off flag value.
945      *
946      * @see #getSendPastNotifications
947      */
setSendPastNotifications(boolean value)948     public void setSendPastNotifications(boolean value) {
949         sendPastNotifications = value;
950     }
951 
952     /**
953      * Tests whether the timer MBean is active.
954      * A timer MBean is marked active when the {@link #start start} method is called.
955      * It becomes inactive when the {@link #stop stop} method is called.
956      * <BR>The default value of the active on/off flag is <CODE>false</CODE>.
957      *
958      * @return <CODE>true</CODE> if the timer MBean is active, <CODE>false</CODE> otherwise.
959      */
isActive()960     public boolean isActive() {
961         return isActive;
962     }
963 
964     /**
965      * Tests whether the list of timer notifications is empty.
966      *
967      * @return <CODE>true</CODE> if the list of timer notifications is empty, <CODE>false</CODE> otherwise.
968      */
isEmpty()969     public synchronized boolean isEmpty() {
970         return (timerTable.isEmpty());
971     }
972 
973     /*
974      * ------------------------------------------
975      *  PRIVATE METHODS
976      * ------------------------------------------
977      */
978 
979     /**
980      * Sends or not past notifications depending on the specified flag.
981      *
982      * @param currentDate The current date.
983      * @param currentFlag The flag indicating if past notifications must be sent or not.
984      */
sendPastNotifications(Date currentDate, boolean currentFlag)985     private synchronized void sendPastNotifications(Date currentDate, boolean currentFlag) {
986 
987         TimerNotification notif;
988         Integer notifID;
989         Date date;
990 
991         ArrayList<Object[]> values =
992             new ArrayList<Object[]>(timerTable.values());
993 
994         for (Object[] obj : values) {
995 
996             // Retrieve the timer notification and the date notification.
997             //
998             notif = (TimerNotification)obj[TIMER_NOTIF_INDEX];
999             notifID = notif.getNotificationID();
1000             date = (Date)obj[TIMER_DATE_INDEX];
1001 
1002             // Update the timer notification while:
1003             //  - the timer notification date is earlier than the current date
1004             //  - the timer notification has not been removed from the timer table.
1005             //
1006             while ( (currentDate.after(date)) && (timerTable.containsKey(notifID)) ) {
1007 
1008                 if (currentFlag == true) {
1009                     if (TIMER_LOGGER.isLoggable(Level.TRACE)) {
1010                         StringBuilder strb = new StringBuilder()
1011                         .append("sending past timer notification:")
1012                         .append("\n\tNotification source = ")
1013                         .append(notif.getSource())
1014                         .append("\n\tNotification type = ")
1015                         .append(notif.getType())
1016                         .append("\n\tNotification ID = ")
1017                         .append(notif.getNotificationID())
1018                         .append("\n\tNotification date = ")
1019                         .append(date)
1020                         .append("\n\tNotification period = ")
1021                         .append(obj[TIMER_PERIOD_INDEX])
1022                         .append("\n\tNotification nb of occurrences = ")
1023                         .append(obj[TIMER_NB_OCCUR_INDEX])
1024                         .append("\n\tNotification executes at fixed rate = ")
1025                         .append(obj[FIXED_RATE_INDEX]);
1026                         TIMER_LOGGER.log(Level.TRACE, strb::toString);
1027                     }
1028                     sendNotification(date, notif);
1029 
1030                     TIMER_LOGGER.log(Level.TRACE, "past timer notification sent");
1031                 }
1032 
1033                 // Update the date and the number of occurrences of the timer notification.
1034                 //
1035                 updateTimerTable(notif.getNotificationID());
1036             }
1037         }
1038     }
1039 
1040     /**
1041      * If the timer notification is not periodic, it is removed from the list of notifications.
1042      * <P>
1043      * If the timer period of the timer notification has a non null periodicity,
1044      * the date of the timer notification is updated by adding the periodicity.
1045      * The associated TimerAlarmClock is updated by setting its timeout to the period value.
1046      * <P>
1047      * If the timer period has a defined number of occurrences, the timer
1048      * notification is updated if the number of occurrences has not yet been reached.
1049      * Otherwise it is removed from the list of notifications.
1050      *
1051      * @param notifID The timer notification identifier to update.
1052      */
updateTimerTable(Integer notifID)1053     private synchronized void updateTimerTable(Integer notifID) {
1054 
1055         // Retrieve the timer notification and the TimerAlarmClock.
1056         //
1057         Object[] obj = timerTable.get(notifID);
1058         Date date = (Date)obj[TIMER_DATE_INDEX];
1059         Long period = (Long)obj[TIMER_PERIOD_INDEX];
1060         Long nbOccurences = (Long)obj[TIMER_NB_OCCUR_INDEX];
1061         Boolean fixedRate = (Boolean)obj[FIXED_RATE_INDEX];
1062         TimerAlarmClock alarmClock = (TimerAlarmClock)obj[ALARM_CLOCK_INDEX];
1063 
1064         if (period.longValue() != 0) {
1065 
1066             // Update the date and the number of occurrences of the timer notification
1067             // and the TimerAlarmClock time out.
1068             // NOTES :
1069             //   nbOccurences = 0 notifies an infinite periodicity.
1070             //   nbOccurences = 1 notifies a finite periodicity that has reached its end.
1071             //   nbOccurences > 1 notifies a finite periodicity that has not yet reached its end.
1072             //
1073             if ((nbOccurences.longValue() == 0) || (nbOccurences.longValue() > 1)) {
1074 
1075                 date.setTime(date.getTime() + period.longValue());
1076                 obj[TIMER_NB_OCCUR_INDEX] = Long.valueOf(java.lang.Math.max(0L, (nbOccurences.longValue() - 1)));
1077                 nbOccurences = (Long)obj[TIMER_NB_OCCUR_INDEX];
1078 
1079                 if (isActive == true) {
1080                   if (fixedRate.booleanValue())
1081                   {
1082                     alarmClock = new TimerAlarmClock(this, date);
1083                     obj[ALARM_CLOCK_INDEX] = (Object)alarmClock;
1084                     timer.schedule(alarmClock, alarmClock.next);
1085                   }
1086                   else
1087                   {
1088                     alarmClock = new TimerAlarmClock(this, period.longValue());
1089                     obj[ALARM_CLOCK_INDEX] = (Object)alarmClock;
1090                     timer.schedule(alarmClock, alarmClock.timeout);
1091                   }
1092                 }
1093                 if (TIMER_LOGGER.isLoggable(Level.TRACE)) {
1094                     TimerNotification notif = (TimerNotification)obj[TIMER_NOTIF_INDEX];
1095                     StringBuilder strb = new StringBuilder()
1096                     .append("update timer notification with:")
1097                     .append("\n\tNotification source = ")
1098                     .append(notif.getSource())
1099                     .append("\n\tNotification type = ")
1100                     .append(notif.getType())
1101                     .append("\n\tNotification ID = ")
1102                     .append(notifID)
1103                     .append("\n\tNotification date = ")
1104                     .append(date)
1105                     .append("\n\tNotification period = ")
1106                     .append(period)
1107                     .append("\n\tNotification nb of occurrences = ")
1108                     .append(nbOccurences)
1109                     .append("\n\tNotification executes at fixed rate = ")
1110                     .append(fixedRate);
1111                     TIMER_LOGGER.log(Level.TRACE, strb::toString);
1112                 }
1113             }
1114             else {
1115                 if (alarmClock != null) {
1116 //                     alarmClock.interrupt();
1117 //                     try {
1118 //                         // Wait until the thread die.
1119 //                         //
1120 //                         alarmClock.join();
1121 //                     } catch (InterruptedException e) {
1122 //                         // Ignore...
1123 //                     }
1124                     alarmClock.cancel();
1125                 }
1126                 timerTable.remove(notifID);
1127             }
1128         }
1129         else {
1130             if (alarmClock != null) {
1131 //                 alarmClock.interrupt();
1132 //                 try {
1133 //                     // Wait until the thread die.
1134 //                     //
1135 //                     alarmClock.join();
1136 //                 } catch (InterruptedException e) {
1137 //                     // Ignore...
1138 //                 }
1139 
1140                    alarmClock.cancel();
1141             }
1142             timerTable.remove(notifID);
1143         }
1144     }
1145 
1146     /*
1147      * ------------------------------------------
1148      *  PACKAGE METHODS
1149      * ------------------------------------------
1150      */
1151 
1152     /**
1153      * This method is called by the timer each time
1154      * the TimerAlarmClock has exceeded its timeout.
1155      *
1156      * @param notification The TimerAlarmClock notification.
1157      */
1158     @SuppressWarnings("deprecation")
notifyAlarmClock(TimerAlarmClockNotification notification)1159     void notifyAlarmClock(TimerAlarmClockNotification notification) {
1160 
1161         TimerNotification timerNotification = null;
1162         Date timerDate = null;
1163 
1164         // Retrieve the timer notification associated to the alarm-clock.
1165         //
1166         TimerAlarmClock alarmClock = (TimerAlarmClock)notification.getSource();
1167 
1168         synchronized(Timer.this) {
1169             for (Object[] obj : timerTable.values()) {
1170                 if (obj[ALARM_CLOCK_INDEX] == alarmClock) {
1171                     timerNotification = (TimerNotification)obj[TIMER_NOTIF_INDEX];
1172                     timerDate = (Date)obj[TIMER_DATE_INDEX];
1173                     break;
1174                 }
1175             }
1176         }
1177 
1178         // Notify the timer.
1179         //
1180         sendNotification(timerDate, timerNotification);
1181 
1182         // Update the notification and the TimerAlarmClock timeout.
1183         //
1184         updateTimerTable(timerNotification.getNotificationID());
1185     }
1186 
1187     /**
1188      * This method is used by the timer MBean to update and send a timer
1189      * notification to all the listeners registered for this kind of notification.
1190      *
1191      * @param timeStamp The notification emission date.
1192      * @param notification The timer notification to send.
1193      */
sendNotification(Date timeStamp, TimerNotification notification)1194     void sendNotification(Date timeStamp, TimerNotification notification) {
1195 
1196         if (TIMER_LOGGER.isLoggable(Level.TRACE)) {
1197             StringBuilder strb = new StringBuilder()
1198             .append("sending timer notification:")
1199             .append("\n\tNotification source = ")
1200             .append(notification.getSource())
1201             .append("\n\tNotification type = ")
1202             .append(notification.getType())
1203             .append("\n\tNotification ID = ")
1204             .append(notification.getNotificationID())
1205             .append("\n\tNotification date = ")
1206             .append(timeStamp);
1207             TIMER_LOGGER.log(Level.TRACE, strb::toString);
1208         }
1209         long curSeqNumber;
1210         synchronized(this) {
1211             sequenceNumber = sequenceNumber + 1;
1212             curSeqNumber = sequenceNumber;
1213         }
1214         synchronized (notification) {
1215             notification.setTimeStamp(timeStamp.getTime());
1216             notification.setSequenceNumber(curSeqNumber);
1217             this.sendNotification((TimerNotification)notification.cloneTimerNotification());
1218         }
1219 
1220         TIMER_LOGGER.log(Level.TRACE, "timer notification sent");
1221     }
1222 }
1223