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