001: /* JFox, the OpenSource J2EE Application Server
002: *
003: * Copyright (C) 2002 huihoo.org
004: * Distributable under GNU LGPL license
005: * See the GNU Lesser General Public License for more details.
006: */
007:
008: package javax.management.timer;
009:
010: import java.util.Map;
011: import java.util.HashMap;
012: import java.util.Iterator;
013: import java.util.Date;
014: import java.util.Vector;
015: import java.util.ArrayList;
016: import java.util.List;
017: import java.util.TimerTask;
018: import javax.management.NotificationBroadcasterSupport;
019: import javax.management.MBeanRegistration;
020: import javax.management.ObjectName;
021: import javax.management.MBeanServer;
022: import javax.management.InstanceNotFoundException;
023: import javax.management.MBeanNotificationInfo;
024:
025: /**
026: * Provides the implementation of the timer MBean.
027: * The timer MBean sends out an alarm at a specified time
028: * that wakes up all the listeners registered to receive timer notifications.
029: * <P>
030: *
031: * This class manages a list of dated timer notifications.
032: * A method allows users to add/remove as many notifications as required.
033: * When a timer notification is emitted by the timer and becomes obsolete,
034: * it is automatically removed from the list of timer notifications.
035: * <BR>Additional timer notifications can be added into regularly repeating notifications.
036: * <P>
037: *
038: * Note:
039: * <OL>
040: * <LI>All notifications before the time when the <CODE>addNotification</CODE> method is called
041: * are ignored, irrespective of the <CODE>sendPastNotifications</CODE> flag.
042: * <LI>When sending timer notifications, the timer updates the notification sequence number
043: * irrespective of the notification type.
044: * <LI>The timer service relies on the system date of the host where the <CODE>Timer</CODE> class is loaded.
045: * Listeners may receive untimely notifications
046: * if their host has a different system date.
047: * To avoid such problems, synchronize the system date of all host machines where timing is needed.
048: * <LI>The default behaviour for periodic notifications is <i>fixed-delay execution</i>, as
049: * specified in {@link java.util.Timer}. In order to use <i>fixed-rate execution</i>, use the
050: * overloaded {@link #addNotification(String, String, Object, Date, long, long, boolean)} method.
051: * <LI>Notification listeners are potentially all executed in the same
052: * thread. Therefore, they should execute rapidly to avoid holding up
053: * other listeners or perturbing the regularity of fixed-delay
054: * executions. See {@link NotificationBroadcasterSupport}.
055: * </OL>
056: *
057: * @author <a href="mailto:young_yy@hotmail.org">Young Yang</a>
058: */
059:
060: public class Timer extends NotificationBroadcasterSupport implements
061: TimerMBean, MBeanRegistration {
062:
063: // notifcationId = > notifacation object
064: private Map notifications = new HashMap();
065: private volatile boolean sendPastNotifications = false;
066: private volatile boolean isActive = false;
067:
068: private volatile int sequenceNumber = 0;
069:
070: private java.util.Timer timer = new java.util.Timer();
071:
072: public Timer() {
073:
074: }
075:
076: public ObjectName preRegister(MBeanServer mbeanserver,
077: ObjectName objectname) throws Exception {
078: if (objectname == null)
079: objectname = new ObjectName(mbeanserver.getDefaultDomain(),
080: "Service", this .toString());
081: return objectname;
082: }
083:
084: public void postRegister(Boolean boolean1) {
085:
086: }
087:
088: public void preDeregister() throws Exception {
089: stop();
090: }
091:
092: public void postDeregister() {
093:
094: }
095:
096: public void start() {
097: if (this .isActive())
098: return;
099: this .checkPastNotifications();
100: isActive = true;
101: }
102:
103: public synchronized void stop() {
104: this .removeAllNotifications();
105: isActive = false;
106:
107: }
108:
109: /**
110: * add a notification to the Timer
111: * @param type The notification type string.
112: * @param message The notification¡¯s detailed message string.
113: * @param userData The notification¡¯s user data object.
114: * @param date The date when the notification will occur. The Timer class includes
115: integer constants for expressing durations in milliseconds, which can then be
116: used to doCreate java.util.Date objects.
117: * @param period The interval in milliseconds between notification occurrences.
118: Repeating notifications are not enabled if this parameter is zero or null.
119: * @param nbOccurences The total number of times that the notification will occur. If
120: the value of this parameter is zero or is not defined (null), and if the period is
121: not zero or null, then the notification will repeat indefinitely.
122: * @return
123: * @throws IllegalArgumentException
124: */
125: public synchronized Integer addNotification(String type,
126: String message, Object userData, Date date, long period,
127: long nbOccurences) throws IllegalArgumentException {
128: return addNotification(type, message, userData, date, period,
129: nbOccurences, false);
130: }
131:
132: public synchronized Integer addNotification(String type,
133: String message, Object userData, Date date, long period)
134: throws IllegalArgumentException {
135: return addNotification(type, message, userData, date, period,
136: 0L);
137: }
138:
139: public synchronized Integer addNotification(String type,
140: String message, Object userData, Date date)
141: throws IllegalArgumentException {
142: return addNotification(type, message, userData, date, 0L);
143: }
144:
145: public Integer addNotification(String type, String message,
146: Object userData, Date date, long period, long nbOccurences,
147: boolean fixedRate) throws IllegalArgumentException {
148: if (date == null)
149: throw new IllegalArgumentException(
150: "Timer notification date cannot be null.");
151: if (period < 0L || nbOccurences < 0L)
152: throw new IllegalArgumentException(
153: "Negative values are not permit for the periodicity");
154:
155: long now = System.currentTimeMillis();
156: long startTime = date.getTime();
157: if (startTime < now) { // date have passed
158: if (period != 0L && nbOccurences > 0L) { // repeat enable
159: while (startTime < now && nbOccurences > 0) {
160: startTime += period;
161: nbOccurences--;
162: }
163: }
164: }
165:
166: if (startTime < now) { // time late
167: throw new IllegalArgumentException(
168: "Timer notification date before the current date");
169: }
170:
171: date.setTime(startTime); // update date
172:
173: Integer sequence = new Integer(++sequenceNumber);
174: TimerNotification notification = new TimerNotification(type,
175: this , sequenceNumber, now, message, sequence);
176: notification.setUserData(userData);
177:
178: JMXTimerTask task = new JMXTimerTask(notification, period,
179: nbOccurences, fixedRate);
180:
181: notifications.put(sequence, task);
182: if (period > 0) {
183: if (fixedRate) {
184: timer.scheduleAtFixedRate(task, date, period);
185: } else {
186: timer.schedule(task, date, period);
187: }
188: } else {
189: // System.out.println("Schedule: " + startTime + ", cuurentTime: " + System.currentTimeMillis());
190: timer.schedule(task, date);
191: }
192:
193: // if(!this.isActive()) this.doStart();
194:
195: return sequence;
196:
197: }
198:
199: public synchronized void removeNotification(Integer id)
200: throws InstanceNotFoundException {
201: if (!notifications.containsKey(id))
202: throw new InstanceNotFoundException(
203: "Timer notification to remove not in the list of notifications");
204:
205: JMXTimerTask task = (JMXTimerTask) notifications.get(id);
206:
207: task.cancel();
208:
209: notifications.remove(id);
210:
211: }
212:
213: public synchronized void removeNotifications(String type)
214: throws InstanceNotFoundException {
215: Vector vector = getNotificationIDs(type);
216: if (vector.isEmpty())
217: throw new InstanceNotFoundException(
218: "Timer notifications to remove not in the list of notifications");
219:
220: Iterator it = vector.iterator();
221: while (it.hasNext()) {
222: Integer id = (Integer) it.next();
223: removeNotification(id);
224: }
225: }
226:
227: public synchronized void removeAllNotifications() {
228: Iterator it = notifications.values().iterator();
229: while (it.hasNext()) {
230: JMXTimerTask task = (JMXTimerTask) it.next();
231: task.cancel();
232: }
233: notifications.clear();
234: sequenceNumber = 0;
235: }
236:
237: public int getNbNotifications() {
238: return notifications.size();
239: }
240:
241: public synchronized Vector getAllNotificationIDs() {
242: return new Vector(notifications.keySet());
243: }
244:
245: public synchronized Vector getNotificationIDs(String type) {
246: List list = new ArrayList();
247: if (type != null) {
248: Iterator it = notifications.values().iterator();
249: while (it.hasNext()) {
250: JMXTimerTask task = (JMXTimerTask) it.next();
251: String _type = task.getNotification().getType();
252: if (type.equals(_type)) {
253: list
254: .add(task.getNotification()
255: .getNotificationID());
256: }
257: }
258: }
259: return new Vector(list);
260:
261: }
262:
263: public String getNotificationType(Integer id) {
264: if (!notifications.containsKey(id))
265: return null;
266:
267: JMXTimerTask task = (JMXTimerTask) notifications.get(id);
268: return task.getNotification().getType();
269: }
270:
271: public String getNotificationMessage(Integer id) {
272: if (!notifications.containsKey(id))
273: return null;
274:
275: JMXTimerTask task = (JMXTimerTask) notifications.get(id);
276: return task.getNotification().getMessage();
277: }
278:
279: public Object getNotificationUserData(Integer id) {
280: if (!notifications.containsKey(id))
281: return null;
282: JMXTimerTask task = (JMXTimerTask) notifications.get(id);
283: return task.getNotification().getUserData();
284: }
285:
286: public Date getDate(Integer id) {
287: if (!notifications.containsKey(id))
288: return null;
289:
290: JMXTimerTask task = (JMXTimerTask) notifications.get(id);
291: return new Date(task.getNotification().getTimeStamp());
292:
293: }
294:
295: public Long getPeriod(Integer id) {
296: if (!notifications.containsKey(id))
297: return null;
298:
299: JMXTimerTask task = (JMXTimerTask) notifications.get(id);
300: return new Long(task.getPeriod());
301: }
302:
303: public Long getNbOccurences(Integer id) {
304: if (!notifications.containsKey(id))
305: return null;
306:
307: JMXTimerTask task = (JMXTimerTask) notifications.get(id);
308: return new Long(task.getNbOccurences());
309: }
310:
311: public Boolean getFixedRate(Integer id) {
312: if (!notifications.containsKey(id))
313: return Boolean.FALSE;
314:
315: JMXTimerTask task = (JMXTimerTask) notifications.get(id);
316: return new Boolean(task.isFixedRate());
317: }
318:
319: public boolean getSendPastNotifications() {
320: return sendPastNotifications;
321: }
322:
323: public void setSendPastNotifications(boolean flag) {
324: sendPastNotifications = flag;
325: }
326:
327: public boolean isActive() {
328: return isActive;
329: }
330:
331: public boolean isEmpty() {
332: return notifications.isEmpty();
333: }
334:
335: public MBeanNotificationInfo[] getNotificationInfo() {
336: MBeanNotificationInfo[] notifInfos = { new MBeanNotificationInfo(
337: new String[] { TimerNotification.TIMER_SCHEDULE },
338: TimerNotification.class.getName(),
339: "Notifications sent by the Timer MBean") };
340: return notifInfos;
341: }
342:
343: // send the past notifications if the sendPastNotifications is true
344: private void checkPastNotifications() {
345:
346: for (Iterator it = new ArrayList(notifications.values())
347: .iterator(); it.hasNext();) {
348: JMXTimerTask task = (JMXTimerTask) it.next();
349: if (task.scheduledExecutionTime() < System
350: .currentTimeMillis()) {
351: if (!this .getSendPastNotifications()) {
352: task.checkOver();
353: } else {
354: task.doTask(); // will checkOver auto
355: }
356: }
357: }
358: }
359:
360: // inner class for scheduled
361: private class JMXTimerTask extends TimerTask {
362: private TimerNotification notification;
363: // nbOccurences=0 means repeat forever
364: private long nbOccurences = 0L;
365: //record the times of this task execute
366: private long executeTimes = 0L;
367: private long period = 0L;
368: private boolean fixedRate = false;
369:
370: private JMXTimerTask(TimerNotification notification,
371: long period, long nbOccurences, boolean fixedRate) {
372: this .notification = notification;
373: this .period = period;
374: this .nbOccurences = nbOccurences;
375: this .fixedRate = fixedRate;
376: }
377:
378: private TimerNotification getNotification() {
379: return notification;
380: }
381:
382: private long getNbOccurences() {
383: return nbOccurences;
384: }
385:
386: private long getPeriod() {
387: return period;
388: }
389:
390: public boolean isFixedRate() {
391: return fixedRate;
392: }
393:
394: public void run() {
395: executeTimes++;
396: if (isActive())
397: doTask();
398: }
399:
400: private void doTask() {
401: sendNotification(notification);
402: checkOver();
403: }
404:
405: private void checkOver() {
406: // if execute times over ,nbOccurences=0 means repeat forever
407: // 1. not repeatable ,2 repeat over
408: if ((period == 0L && executeTimes > 0)
409: || (nbOccurences != 0 && executeTimes > nbOccurences)) {
410: try {
411: removeNotification(notification.getNotificationID());
412: } catch (InstanceNotFoundException e) {
413: e.printStackTrace();
414: }
415: } else { // update notification sequenceNumber
416: notification.setSequenceNumber(++sequenceNumber);
417: }
418: }
419: }
420: }
|