001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package javax.management.timer;
023:
024: import java.util.Date;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.Vector;
028:
029: import javax.management.InstanceNotFoundException;
030: import javax.management.MBeanRegistration;
031: import javax.management.MBeanServer;
032: import javax.management.NotificationBroadcasterSupport;
033: import javax.management.ObjectName;
034:
035: import org.jboss.logging.Logger;
036: import org.jboss.mx.util.RunnableScheduler;
037: import org.jboss.mx.util.SchedulableRunnable;
038:
039: /**
040: * The timer service.
041: *
042: * @author <a href="mailto:Adrian.Brock@HappeningTimes.com">Adrian Brock</a>
043: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
044: * @version $Revision: 57200 $
045: */
046: public class Timer extends NotificationBroadcasterSupport implements
047: TimerMBean, MBeanRegistration {
048: // logging support
049: private static Logger log = Logger.getLogger(Timer.class);
050:
051: // Constants -----------------------------------------------------
052:
053: /** The number of milliseconds in one second. */
054: public static final long ONE_SECOND = 1000;
055:
056: /** The number of milliseconds in one minute. */
057: public static final long ONE_MINUTE = ONE_SECOND * 60;
058:
059: /** The number of milliseconds in one hour. */
060: public static final long ONE_HOUR = ONE_MINUTE * 60;
061:
062: /** The number of milliseconds in one day. */
063: public static final long ONE_DAY = ONE_HOUR * 24;
064:
065: /** The number of milliseconds in one week. */
066: public static final long ONE_WEEK = ONE_DAY * 7;
067:
068: /** Don't send notifications at initial start up. */
069: private static final int SEND_NO = 0;
070:
071: /** Send all past notifications at initial start up. */
072: private static final int SEND_START = 1;
073:
074: /** Normal operation sending */
075: private static final int SEND_NORMAL = 2;
076:
077: // Attributes ----------------------------------------------------
078:
079: /** The next notification id. */
080: int nextId = 0;
081:
082: /** The next notification sequence number. */
083: long sequenceNumber = 0;
084:
085: /** The send past events attribute. */
086: boolean sendPastNotifications = false;
087:
088: /** Whether the service is active. */
089: boolean active = false;
090:
091: /** Our object name. */
092: ObjectName objectName;
093:
094: /** The registered notifications. */
095: HashMap notifications = new HashMap();
096:
097: /** The scheduler */
098: private RunnableScheduler scheduler = new RunnableScheduler();
099:
100: // Static --------------------------------------------------------
101:
102: // Constructors --------------------------------------------------
103:
104: // Public --------------------------------------------------------
105:
106: // TimerMBean implementation -------------------------------------
107:
108: public Integer addNotification(String type, String message,
109: Object userData, Date date) throws IllegalArgumentException {
110: return addNotification(type, message, userData, date, 0);
111: }
112:
113: public Integer addNotification(String type, String message,
114: Object userData, Date date, long period)
115: throws IllegalArgumentException {
116: return addNotification(type, message, userData, date, period, 0);
117: }
118:
119: public Integer addNotification(String type, String message,
120: Object userData, Date date, long period, long occurences)
121: throws IllegalArgumentException {
122: return addNotification(type, message, userData, date, period,
123: occurences, false);
124: }
125:
126: /**
127: * Creates a new timer notification with the specified type, message and userData and inserts it into the list of notifications with a given date, period and number of occurences.
128: * <p/>
129: * If the timer notification to be inserted has a date that is before the current date, the method behaves as if the specified date were the current date.
130: * For once-off notifications, the notification is delivered immediately.
131: * For periodic notifications, the first notification is delivered immediately and the subsequent ones are spaced as specified by the period parameter.
132: * <p/>
133: * Note that once the timer notification has been added into the list of notifications, its associated date, period and number of occurences cannot be updated.
134: * <p/>
135: * In the case of a periodic notification, the value of parameter fixedRate is used to specify the execution scheme, as specified in Timer.
136: *
137: * @param type The timer notification type.
138: * @param message The timer notification detailed message.
139: * @param userData The timer notification user data object.
140: * @param date The date when the notification occurs.
141: * @param period The period of the timer notification (in milliseconds).
142: * @param nbOccurences The total number the timer notification will be emitted.
143: * @param fixedRate If true and if the notification is periodic, the notification is scheduled with a fixed-rate execution scheme. If false and if the notification is periodic, the notification is scheduled with a fixed-delay execution scheme. Ignored if the notification is not periodic.
144: * @return The identifier of the new created timer notification.
145: * @throws IllegalArgumentException The period or the number of occurences is negative
146: */
147: public Integer addNotification(String type, String message,
148: Object userData, Date date, long period, long nbOccurences,
149: boolean fixedRate) throws IllegalArgumentException {
150: // Generate the next id.
151: int newId = 0;
152: newId = ++nextId;
153: Integer id = new Integer(newId);
154:
155: // Validate and create the registration.
156: RegisteredNotification rn = new RegisteredNotification(id,
157: type, message, userData, date, period, nbOccurences,
158: fixedRate);
159:
160: // Add the registration.
161: synchronized (notifications) {
162: notifications.put(id, rn);
163: rn.setNextRun(rn.nextDate);
164: rn.setScheduler(scheduler);
165: }
166:
167: return id;
168: }
169:
170: public Vector getAllNotificationIDs() {
171: synchronized (notifications) {
172: return new Vector(notifications.keySet());
173: }
174: }
175:
176: public Date getDate(Integer id) {
177: // Make sure there is a registration
178: RegisteredNotification rn = (RegisteredNotification) notifications
179: .get(id);
180: if (rn == null)
181: return null;
182:
183: // Return a copy of the date.
184: return new Date(rn.startDate);
185: }
186:
187: public int getNbNotifications() {
188: return notifications.size();
189: }
190:
191: public Long getNbOccurences(Integer id) {
192: // Make sure there is a registration
193: RegisteredNotification rn = (RegisteredNotification) notifications
194: .get(id);
195: if (rn == null)
196: return null;
197:
198: // Return a copy of the occurences.
199: return new Long(rn.occurences);
200: }
201:
202: /**
203: * Gets a copy of the flag indicating whether a peridic notification is executed at fixed-delay or at fixed-rate.
204: *
205: * @param id The timer notification identifier.
206: * @return A copy of the flag indicating whether a peridic notification is executed at fixed-delay or at fixed-rate.
207: */
208: public Boolean getFixedRate(Integer id) {
209: // Make sure there is a registration
210: RegisteredNotification rn = (RegisteredNotification) notifications
211: .get(id);
212: if (rn == null)
213: return null;
214:
215: // Return a copy of the fixedRate
216: return new Boolean(rn.fixedRate);
217: }
218:
219: public Vector getNotificationIDs(String type) {
220: Vector result = new Vector();
221:
222: // Loop through the notifications looking for the passed type.
223: synchronized (notifications) {
224: Iterator iterator = notifications.values().iterator();
225: while (iterator.hasNext()) {
226: RegisteredNotification rn = (RegisteredNotification) iterator
227: .next();
228: if (rn.type.equals(type))
229: result.add(rn.id);
230: }
231: }
232:
233: return result;
234: }
235:
236: public String getNotificationMessage(Integer id) {
237: // Make sure there is a registration
238: RegisteredNotification rn = (RegisteredNotification) notifications
239: .get(id);
240: if (rn == null)
241: return null;
242:
243: // Return the message
244: return rn.message;
245: }
246:
247: public String getNotificationType(Integer id) {
248: // Make sure there is a registration
249: RegisteredNotification rn = (RegisteredNotification) notifications
250: .get(id);
251: if (rn == null)
252: return null;
253:
254: // Return the type.
255: return rn.type;
256: }
257:
258: public Object getNotificationUserData(Integer id) {
259: // Make sure there is a registration
260: RegisteredNotification rn = (RegisteredNotification) notifications
261: .get(id);
262: if (rn == null)
263: return null;
264:
265: // Return the user data.
266: return rn.userData;
267: }
268:
269: public Long getPeriod(Integer id) {
270: // Make sure there is a registration
271: RegisteredNotification rn = (RegisteredNotification) notifications
272: .get(id);
273: if (rn == null)
274: return null;
275:
276: // Return a copy of the period
277: return new Long(rn.period);
278: }
279:
280: public boolean getSendPastNotifications() {
281: return sendPastNotifications;
282: }
283:
284: public boolean isActive() {
285: return active;
286: }
287:
288: public boolean isEmpty() {
289: return notifications.isEmpty();
290: }
291:
292: public void removeAllNotifications() {
293: // Remove the notifications
294: synchronized (notifications) {
295: Iterator iterator = notifications.values().iterator();
296: while (iterator.hasNext()) {
297: RegisteredNotification rn = (RegisteredNotification) iterator
298: .next();
299: rn.setScheduler(null);
300: iterator.remove();
301: }
302: }
303:
304: // The spec says to reset the identifiers, seems like a bad idea to me
305: synchronized (this ) {
306: nextId = 0;
307: }
308: }
309:
310: public void removeNotification(Integer id)
311: throws InstanceNotFoundException {
312:
313: log.debug("removeNotification: " + objectName + ",id=" + id);
314:
315: // Check if there is a notification.
316: synchronized (notifications) {
317: RegisteredNotification rn = (RegisteredNotification) notifications
318: .get(id);
319: if (rn == null)
320: throw new InstanceNotFoundException(
321: "No notification id : " + id.toString());
322:
323: // Remove the notification
324: rn.setScheduler(null);
325: notifications.remove(id);
326: }
327: }
328:
329: public void removeNotifications(String type)
330: throws InstanceNotFoundException {
331: boolean found = false;
332:
333: log.debug("removeNotifications: " + objectName + ",type="
334: + type);
335:
336: // Loop through the notifications removing the passed type.
337: synchronized (notifications) {
338: Iterator iterator = notifications.values().iterator();
339: while (iterator.hasNext()) {
340: RegisteredNotification rn = (RegisteredNotification) iterator
341: .next();
342: if (rn.type.equals(type)) {
343: rn.setScheduler(null);
344: iterator.remove();
345: found = true;
346: }
347: }
348: }
349:
350: // The spec says to through an exception when nothing removed.
351: if (found == false)
352: throw new InstanceNotFoundException(
353: "Nothing registered for type: " + type);
354: }
355:
356: public void setSendPastNotifications(boolean value) {
357: log.debug("setSendPastNotifications: " + objectName + ",value="
358: + value);
359: sendPastNotifications = value;
360: }
361:
362: public synchronized void start() {
363: // Ignore if already active
364: if (active == true)
365: return;
366: active = true;
367:
368: log.debug("start: " + objectName + " at " + new Date());
369:
370: // Perform the initial sends, for past notifications send missed events
371: // otherwise ignore them
372: synchronized (notifications) {
373: Iterator iterator = notifications.values().iterator();
374: while (iterator.hasNext()) {
375: RegisteredNotification rn = (RegisteredNotification) iterator
376: .next();
377: if (sendPastNotifications)
378: rn.sendType = SEND_START;
379: else
380: rn.sendType = SEND_NO;
381: sendNotifications(rn);
382: rn.sendType = SEND_NORMAL;
383: }
384: }
385:
386: // Start 'em up
387: scheduler.start();
388: }
389:
390: public synchronized void stop() {
391: // Ignore if not active
392: if (active == false)
393: return;
394:
395: log.debug("stop: " + objectName + ",now=" + new Date());
396:
397: // Stop the threads
398: active = false;
399: scheduler.stop();
400: }
401:
402: // MBeanRegistrationImplementation overrides ---------------------
403:
404: public ObjectName preRegister(MBeanServer server,
405: ObjectName objectName) throws Exception {
406: // Save the object name
407: this .objectName = objectName;
408:
409: // Use the passed object name.
410: return objectName;
411: }
412:
413: public void postRegister(Boolean registrationDone) {
414: }
415:
416: public void preDeregister() throws Exception {
417: // Stop the timer before deregistration.
418: stop();
419: }
420:
421: public void postDeregister() {
422: }
423:
424: // Package protected ---------------------------------------------
425:
426: // Protected -----------------------------------------------------
427:
428: // Private -------------------------------------------------------
429:
430: /**
431: * Send any outstanding notifications.
432: *
433: * @param rn the registered notification to send.
434: */
435: private void sendNotifications(RegisteredNotification rn) {
436: // Keep going until we have done all outstanding notifications.
437: // The loop ends when not active, or there are no outstanding
438: // notifications.
439: // REVIEW: In practice for normal operation it never loops. We
440: // ignore sends that we have missed. This avoids problems where
441: // the notification takes longer than the period. Correct???
442: while (isActive() && rn.nextDate != 0
443: && rn.nextDate <= System.currentTimeMillis()) {
444: // Do we actually send it?
445: // Yes, unless start and not sending past notifications.
446: if (rn.sendType != SEND_NO) {
447: long seq = 0;
448: synchronized (this ) {
449: seq = ++sequenceNumber;
450: }
451:
452: log.debug("sendNotification: " + rn);
453: TimerNotification tn = new TimerNotification(rn.type,
454: objectName, seq, rn.nextDate, rn.message, rn.id);
455: tn.setUserData(rn.userData);
456: sendNotification(tn);
457: }
458: // Calculate the next date.
459: // Except for when we are sending past notifications at start up,
460: // it cannot be in the future.
461: do {
462: // If no next run, remove it sets the next date to zero.
463: if (rn.calcNextDate() == false) {
464: synchronized (notifications) {
465: log.debug("remove: " + rn);
466: notifications.remove(rn.id);
467: }
468: }
469: } while (isActive() && rn.sendType != SEND_START
470: && rn.nextDate != 0 && rn.occurences == 0
471: && rn.nextDate < System.currentTimeMillis());
472: }
473:
474: if (rn.nextDate != 0)
475: rn.setNextRun(rn.nextDate);
476: }
477:
478: // Inner classes -------------------------------------------------
479:
480: /**
481: * A registered notification. These run as separate threads.
482: */
483: private class RegisteredNotification extends SchedulableRunnable {
484: // Attributes ----------------------------------------------------
485:
486: /** The notification id. */
487: public Integer id;
488:
489: /** The notification type. */
490: public String type;
491:
492: /** The message. */
493: public String message;
494:
495: /** The user data. */
496: public Object userData;
497:
498: /** The start date. */
499: public long startDate;
500:
501: /** The period. */
502: public long period;
503:
504: /** The maximum number of occurences. */
505: public long occurences;
506:
507: /** The flag to indicate fixedRate notifications, or fixedDelay (default) */
508: public boolean fixedRate;
509:
510: /** The send type, no send, past notifications or normal */
511: public int sendType = SEND_NORMAL;
512:
513: /** The next run date */
514: public long nextDate = 0;
515:
516: // Constructors --------------------------------------------------
517:
518: /**
519: * The default constructor.
520: *
521: * @param id the notification id.
522: * @param type the notification type.
523: * @param message the notification's message string.
524: * @param userData the notification's user data.
525: * @param startDate the date/time the notification will occur.
526: * @param period the repeat period in milli-seconds. Passing zero means
527: * no repeat.
528: * @param occurences the maximum number of repeats. When the period is not
529: * zero and this parameter is zero, it will repeat indefinitely.
530: * @param fixedRate If true and if the notification is periodic, the notification
531: * is scheduled with a fixed-rate execution scheme. If false and if the notification
532: * is periodic, the notification is scheduled with a fixed-delay execution scheme.
533: * Ignored if the notification is not periodic.
534: *
535: * @exception IllegalArgumentException when the date is before the current
536: * date, the period is negative or the number of repeats is
537: * negative.
538: */
539: public RegisteredNotification(Integer id, String type,
540: String message, Object userData, Date startDate,
541: long period, long occurences, boolean fixedRate)
542: throws IllegalArgumentException {
543: // Basic validation
544: if (startDate == null)
545: throw new IllegalArgumentException("Null Date");
546: if (period < 0)
547: throw new IllegalArgumentException("Negative Period");
548: if (occurences < 0)
549: throw new IllegalArgumentException(
550: "Negative Occurences");
551:
552: this .startDate = startDate.getTime();
553: if (startDate.getTime() < System.currentTimeMillis()) {
554: log.debug("startDate [" + startDate
555: + "] in the past, set to now");
556: this .startDate = System.currentTimeMillis();
557: }
558:
559: // Remember the values
560: this .id = id;
561: this .type = type;
562: this .message = message;
563: this .userData = userData;
564: this .period = period;
565: this .occurences = occurences;
566: this .fixedRate = fixedRate;
567:
568: this .nextDate = this .startDate;
569:
570: String msgStr = "new " + this .toString();
571: log.debug(msgStr);
572: }
573:
574: // Public --------------------------------------------------------
575:
576: /**
577: * Calculate the next notification date. Add on the period until
578: * the number of occurences is exhausted.
579: *
580: * @return false when there are no more occurences, true otherwise.
581: */
582: boolean calcNextDate() {
583: // No period, we've finished
584: if (period == 0) {
585: nextDate = 0;
586: return false;
587: }
588:
589: // Limited number of repeats have we finished?
590: if (occurences != 0 && --occurences == 0) {
591: nextDate = 0;
592: return false;
593: }
594:
595: // Calculate the next occurence
596: if (fixedRate)
597: nextDate += period;
598: else
599: // fixed delay
600: nextDate = System.currentTimeMillis() + period;
601:
602: return true;
603: }
604:
605: // SchedulableRunnable overrides ---------------------------------
606:
607: /**
608: * Send the notifications.
609: */
610: public void doRun() {
611: // Send any notifications
612: sendNotifications(this );
613: }
614:
615: public String toString() {
616: return " RegisteredNotification: [timer=" + objectName
617: + ",id=" + id + ",startDate=" + new Date(startDate)
618: + ",period=" + period + ",occurences=" + occurences
619: + ",fixedRate=" + fixedRate + ",nextDate="
620: + new Date(nextDate) + "]";
621: }
622: }
623: }
|