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