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