User: ejort
Date: 01/12/17 00:19:07
Modified: src/main/javax/management/timer Timer.java TimerMBean.java
Log:
Reimplement the Timer Service to use one thread per notification.
Fixed the synchronization, still some checks required there.
Fixed a spelling mistake. occurances -> occurences
Added a timer test class.
Revision Changes Path
1.2 +186 -156 jmx/src/main/javax/management/timer/Timer.java
Index: Timer.java
===================================================================
RCS file: /cvsroot/jboss/jmx/src/main/javax/management/timer/Timer.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- Timer.java 2001/12/16 19:38:39 1.1
+++ Timer.java 2001/12/17 08:19:07 1.2
@@ -19,7 +19,7 @@
import javax.management.NotificationBroadcasterSupport;
import javax.management.ObjectName;
-// TODO Fix the synchronization
+// REVIEW: Check synchronization
/**
* The timer service.
@@ -54,7 +54,22 @@
* The number of milliseconds in one week.
*/
public static final long ONE_WEEK = ONE_DAY * 7;
-
+
+ /**
+ * Don't send notifications at initial start up.
+ */
+ private static final int SEND_NO = 0;
+
+ /**
+ * Send all past notifications at initial start up.
+ */
+ private static final int SEND_START = 1;
+
+ /**
+ * Normal operation sending
+ */
+ private static final int SEND_NORMAL = 2;
+
// Attributes ----------------------------------------------------
/**
@@ -68,11 +83,6 @@
long sequenceNumber = 0;
/**
- * The date next run of the notifications, null when nothing to run.
- */
- long nextRunDate = 0;
-
- /**
* The send past events attribute.
*/
boolean sendPastNotifications = false;
@@ -83,15 +93,10 @@
boolean active = false;
/**
- * The notification thread.
- */
- Thread notificationThread = null;
-
- /**
* The registered notifications.
*/
Hashtable notifications = new Hashtable();
-
+
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
@@ -114,8 +119,9 @@
return addNotification(type, message, userData, date, period, 0);
}
- public Integer addNotification(String type, String message, Object userData,
- Date date, long period, long occurances)
+ // REVIEW: Synchronized because of active/thread start
+ public synchronized Integer addNotification(String type, String message,
+ Object userData, Date date, long period, long occurences)
throws IllegalArgumentException
{
// Generate the next id.
@@ -127,16 +133,16 @@
Integer id = new Integer(newId);
// Validate and create the registration.
- RegisteredNotification rn =
+ RegisteredNotification rn =
new RegisteredNotification(id, type, message, userData, date, period,
- occurances);
+ occurences);
// Add the registration.
notifications.put(id, rn);
- // Wake the background thread up
- if (notificationThread != null)
- notificationThread.interrupt();
+ // If we are active, start the thread
+ if (active)
+ rn.start();
return id;
}
@@ -149,7 +155,7 @@
public Date getDate(Integer id)
{
// Make sure there is a registration
- RegisteredNotification rn = (RegisteredNotification) notifications.get(id);
+ RegisteredNotification rn = (RegisteredNotification)notifications.get(id);
if (rn == null)
return null;
@@ -162,15 +168,15 @@
return notifications.size();
}
- public Long getNbOccurances(Integer id)
+ public Long getNbOccurences(Integer id)
{
// Make sure there is a registration
- RegisteredNotification rn = (RegisteredNotification) notifications.get(id);
+ RegisteredNotification rn = (RegisteredNotification)notifications.get(id);
if (rn == null)
return null;
- // Return a copy of the occurances.
- return new Long(rn.occurances);
+ // Return a copy of the occurences.
+ return new Long(rn.occurences);
}
public Vector getNotificationIDs(String type)
@@ -191,7 +197,7 @@
public String getNotificationMessage(Integer id)
{
// Make sure there is a registration
- RegisteredNotification rn = (RegisteredNotification) notifications.get(id);
+ RegisteredNotification rn = (RegisteredNotification)notifications.get(id);
if (rn == null)
return null;
@@ -202,7 +208,7 @@
public String getNotificationType(Integer id)
{
// Make sure there is a registration
- RegisteredNotification rn = (RegisteredNotification) notifications.get(id);
+ RegisteredNotification rn = (RegisteredNotification)notifications.get(id);
if (rn == null)
return null;
@@ -213,7 +219,7 @@
public Object getNotificationUserData(Integer id)
{
// Make sure there is a registration
- RegisteredNotification rn = (RegisteredNotification) notifications.get(id);
+ RegisteredNotification rn = (RegisteredNotification)notifications.get(id);
if (rn == null)
return null;
@@ -224,7 +230,7 @@
public Long getPeriod(Integer id)
{
// Make sure there is a registration
- RegisteredNotification rn = (RegisteredNotification) notifications.get(id);
+ RegisteredNotification rn = (RegisteredNotification)notifications.get(id);
if (rn == null)
return null;
@@ -247,24 +253,48 @@
return notifications.isEmpty();
}
- public void removeAllNotifications()
+ // REVIEW: Synchronized because of active/thread stop and enumeration
+ public synchronized void removeAllNotifications()
{
- notifications.clear();
+ RegisteredNotification rn = null;
+
+ // Loop through the notifications removing everything.
+ for (Enumeration e = notifications.elements(); e.hasMoreElements();)
+ {
+ rn = (RegisteredNotification) e.nextElement();
+ notifications.remove(rn.id);
+
+ // Stop the thread
+ rn.nextDate = 0;
+ if (active)
+ rn.interrupt();
+ }
+
+ // The spec says to reset the identifiers, seems like a bad idea to me
+ nextId = 0;
}
- public void removeNotification(Integer id)
+ // REVIEW: Synchronized because of active/thread stop and enumeration
+ public synchronized void removeNotification(Integer id)
throws InstanceNotFoundException
{
// Check if there is a notification.
- RegisteredNotification rn = (RegisteredNotification) notifications.get(id);
+ RegisteredNotification rn = (RegisteredNotification)notifications.get(id);
if (rn == null)
- throw new InstanceNotFoundException("No notification id : " + id.toString());
+ throw new InstanceNotFoundException("No notification id : " +
+ id.toString());
// Remove the notification
notifications.remove(id);
+
+ // Stop the thread
+ rn.nextDate = 0;
+ if (active)
+ rn.interrupt();
}
- public void removeNotifications(String type)
+ // REVIEW: Synchronized because of active/thread stop and enumeration
+ public synchronized void removeNotifications(String type)
throws InstanceNotFoundException
{
RegisteredNotification rn = null;
@@ -278,12 +308,18 @@
{
notifications.remove(rn.id);
found = true;
+
+ // Stop the thread
+ rn.nextDate = 0;
+ if (active)
+ rn.interrupt();
}
}
// The spec says to through an error when nothing removed.
if (found == false)
- throw new InstanceNotFoundException("Nothing registered for type: " + type);
+ throw new InstanceNotFoundException("Nothing registered for type: " +
+ type);
}
public void setSendPastNotifications(boolean value)
@@ -291,31 +327,39 @@
sendPastNotifications = value;
}
- public void start()
+ // REVIEW: Synchronized because of active/thread stop and enumeration
+ public synchronized void start()
{
// Ignore if already active
if (active)
return;
active = true;
-
- // Do the send past notification processing
- sendNotifications(sendPastNotifications);
- // Start the thread
- notificationThread = new Thread(new PollingThread());
- notificationThread.setDaemon(true);
- notificationThread.start();
+ // Start the threads for each notification
+ for (Enumeration e = notifications.elements(); e.hasMoreElements();)
+ {
+ ((Thread) e.nextElement()).start();
+ }
}
- public void stop()
+ // REVIEW: Synchronized because of active/thread stop and enumeration
+ public synchronized void stop()
{
- // Stop the thread
+ // Ignore if not active
+ if (!active)
+ return;
+
+ // Stop the threads
active = false;
- if (notificationThread != null)
- notificationThread.interrupt();
+
+ // Ask each thread to stop
+ for (Enumeration e = notifications.elements(); e.hasMoreElements();)
+ {
+ ((Thread) e.nextElement()).interrupt();
+ }
}
- // NotificationBroadcasterSupport overrides ----------------------
+ // MBeanRegistrationImplementation overrides ---------------------
public ObjectName preRegister(MBeanServer server, ObjectName objectName)
throws Exception
@@ -346,83 +390,61 @@
// Private -------------------------------------------------------
/**
- * Recalculate the next run.
- */
- private void reschedule()
- {
- // No need to reschedule when not active
- if (!isActive())
- return;
-
- RegisteredNotification rn = null;
- long calcNextRunDate = 0;
-
- // Loop through the registered notifications to find the next run.
- for (Enumeration e = notifications.elements(); e.hasMoreElements();)
- {
- rn = (RegisteredNotification) e.nextElement();
- if (calcNextRunDate == 0 || rn.nextDate < calcNextRunDate)
- calcNextRunDate = rn.nextDate;
- }
-
- // Tell the thread about any changed next run date
- if (nextRunDate != calcNextRunDate)
- {
- nextRunDate = calcNextRunDate;
- }
- }
-
- /**
* Send any outstanding notifications.
*
+ * @param rn the registered notification to send.
* @param doSend whether the send operations should be performed.
- * Pass true to perform the sends.
- * @return true when a notification was sent, false otherwise.
- */
- private void sendNotifications(boolean doSend)
- {
- RegisteredNotification rn = null;
-
- // Keep going until we have done any outstanding notifications
- boolean notificationSent = false;
- do
+ * Pass SEND_NO for startup and not sending past notification,
+ * pass SEND_START for startup and sending past notifications,
+ * pass SEND_NORMAL for normal operation.
+ */
+ private void sendNotifications(RegisteredNotification rn, int doSend)
+ {
+ // Keep going until we have done all outstanding notifications.
+ // The loop ends when not active, or there are no outstanding
+ // notifications.
+ // REVIEW: In practice for normal operation it never loops. We
+ // ignore sends that we have missed. This avoids problems where
+ // the notification takes longer than the period. Correct???
+ while (isActive() && rn.nextDate != 0
+ && rn.nextDate <= System.currentTimeMillis())
{
- notificationSent = false;
-
- // Loop through the registered notifications.
- for (Enumeration e = notifications.elements(); e.hasMoreElements();)
+ // Do we actually send it?
+ // Yes, unless start and not sending past notifications.
+ if (doSend != SEND_NO)
{
- rn = (RegisteredNotification) e.nextElement();
-
- // Notification required
- if (rn.nextDate <= System.currentTimeMillis())
+ long seq = 0;
+ synchronized (this)
{
- long seq = 0;
- synchronized (this)
- {
- seq = ++sequenceNumber;
- }
- sendNotification(new TimerNotification(rn.type, this, seq,
- System.currentTimeMillis(), rn.message, rn.id));
- notificationSent = true;
-
- // If no next run, remove it
- if (!rn.calcNextDate())
- notifications.remove(rn.id);
+ seq = ++sequenceNumber;
}
+ sendNotification(new TimerNotification(rn.type, this, seq,
+ System.currentTimeMillis(), rn.message, rn.id));
}
- } while (notificationSent == true);
- // Reschedule the next run
- reschedule();
+ // Calculate the next date.
+ // Except for we are sending past notifications at start up,
+ // it cannot be in the future.
+ do
+ {
+ // If no next run, remove it sets the next date to zero.
+ if (!rn.calcNextDate())
+ {
+ notifications.remove(rn.id);
+ }
+ }
+ while (isActive() && doSend != SEND_START && rn.nextDate != 0
+ && rn.nextDate < System.currentTimeMillis());
+ }
}
// Inner classes -------------------------------------------------
/**
- * A registered notification.
+ * A registered notification. These run as separate threads.
*/
private class RegisteredNotification
+ extends Thread
{
// Attributes ----------------------------------------------------
@@ -457,15 +479,15 @@
public long period;
/**
- * The maximum number of occurances.
+ * The maximum number of occurences.
*/
- public long occurances;
+ public long occurences;
/**
* The next notification date.
*/
public long nextDate;
-
+
// Constructors --------------------------------------------------
/**
@@ -478,16 +500,16 @@
* @param date the date/time the notification will occur.
* @param period the repeat period in milli-seconds. Passing zero means
* no repeat.
- * @param occurances the maximum number of repeats. When the period is not
+ * @param occurences the maximum number of repeats. When the period is not
* zero and this parameter is zero, it will repeat indefinitely.
* @return the notification id for this notification.
* @exception IllegalArgumentException when the date is before the current
* date, the period is negative or the number of repeats is
* negative.
- */
- public RegisteredNotification(Integer id, String type, String message,
+ */
+ public RegisteredNotification(Integer id, String type, String message,
Object userData, Date startDate,
- long period, long occurances)
+ long period, long occurences)
throws IllegalArgumentException
{
// Basic validation
@@ -495,8 +517,8 @@
throw new IllegalArgumentException("Null Date");
if (period < 0)
throw new IllegalArgumentException("Negative Period");
- if (occurances < 0)
- throw new IllegalArgumentException("Negative Occurances");
+ if (occurences < 0)
+ throw new IllegalArgumentException("Negative Occurences");
// Remember the values
this.id = id;
@@ -505,16 +527,17 @@
this.userData = userData;
this.startDate = startDate.getTime();
this.period = period;
- this.occurances = occurances;
+ this.occurences = occurences;
// Can we get a next date in the future
nextDate = this.startDate;
while (nextDate < System.currentTimeMillis())
{
- // If we have no more occurances its an error
+ // If we have no more occurences its an error
if (!calcNextDate())
throw new IllegalArgumentException("Requested notification(s) " +
"in the past.");
+
}
}
@@ -522,67 +545,74 @@
/**
* Calculate the next notification date. Add on the period until
- * the number of occurances is exhausted.
+ * the number of occurences is exhausted.
*
- * @return false when there are no more occurances, true otherwise.
+ * @return false when there are no more occurences, true otherwise.
*/
synchronized boolean calcNextDate()
{
// No period, we've finished
if (period == 0)
+ {
+ nextDate = 0;
return false;
+ }
// Limited number of repeats have we finished?
- if (occurances !=0 && --occurances == 0)
+ if (occurences != 0 && --occurences == 0)
+ {
+ nextDate = 0;
return false;
+ }
- // Calculate the next occurance
+ // Calculate the next occurence
nextDate += period;
return true;
}
- }
- /**
- * The notification thread polling.
- */
- private class PollingThread
- implements Runnable
- {
- // Attributes ----------------------------------------------------
-
- // Constructors --------------------------------------------------
+ // Thread overrides ----------------------------------------------
- // Public --------------------------------------------------------
-
/**
* Wait until the next run date or until we are interrupted by
- * a reschedule. When active, check the registered notifications
- * to see whether notifications should be sent.
+ * a remove or stop. When active, send any outstanding notifications.
+ *
*/
public void run()
{
+ // Work out the startup send mode
+ int doSend = SEND_NO;
+ if (sendPastNotifications)
+ doSend = SEND_START;
+
// Keep going until we are stopped
- while (isActive())
+ while (isActive() && nextDate != 0)
{
// Send any notifications
- sendNotifications(true);
+ sendNotifications(this, doSend);
- // Wait until the next run or we are interrupted
- try
- {
- if (nextRunDate == 0)
- Thread.currentThread().sleep(30*ONE_MINUTE);
- else
- {
- long waitMillis = nextRunDate - System.currentTimeMillis();
- if (waitMillis > 0)
- Thread.currentThread().sleep(waitMillis);
- }
- }
- catch (InterruptedException ignored)
- {
- }
+ // We have done the initial send, switch to normal mode
+ doSend = SEND_NORMAL;
+
+ // Wait until we the next notification
+ doWait();
+ }
+ }
+
+ /**
+ * Wait until the next run date or until we are interrupted by
+ * remove or stop.
+ */
+ private synchronized void doWait()
+ {
+ try
+ {
+ long waitMillis = nextDate - System.currentTimeMillis();
+ if (waitMillis > 0)
+ wait(waitMillis);
+ }
+ catch (InterruptedException ignored)
+ {
}
}
}
1.2 +7 -7 jmx/src/main/javax/management/timer/TimerMBean.java
Index: TimerMBean.java
===================================================================
RCS file: /cvsroot/jboss/jmx/src/main/javax/management/timer/TimerMBean.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- TimerMBean.java 2001/12/16 19:38:39 1.1
+++ TimerMBean.java 2001/12/17 08:19:07 1.2
@@ -15,7 +15,7 @@
* The timer service MBean interface. <p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Adrian Brock</a>
- * @version $Revision: 1.1 $
+ * @version $Revision: 1.2 $
*
*/
@@ -68,11 +68,11 @@
/**
* Creates a new timer notification for a specific date/time, with an
- * optional repeat period and a maximum number of occurances.<p>
+ * optional repeat period and a maximum number of occurences.<p>
*
* If the date and time is before the the current date and time the period
* is repeatedly added until a date after the current date and time is
- * found. If the number of occurances is exceeded before the
+ * found. If the number of occurences is exceeded before the
* current date and time is reached, an IllegalArgumentException is raised.
*
* @param type the notification type.
@@ -81,7 +81,7 @@
* @param date the date/time the notification will occur.
* @param period the repeat period in milli-seconds. Passing zero means
* no repeat.
- * @param occurances the maximum number of repeats. When the period is not
+ * @param occurences the maximum number of repeats. When the period is not
* zero and this parameter is zero, it will repeat indefinitely.
* @return the notification id for this notification.
* @exception IllegalArgumentException when the date is before the current
@@ -89,7 +89,7 @@
* negative.
*/
public Integer addNotification(String type, String message, Object userData,
- Date date, long period, long occurances)
+ Date date, long period, long occurences)
throws IllegalArgumentException;
/**
@@ -117,14 +117,14 @@
public int getNbNotifications();
/**
- * Retrieves a copy of the maximum notification occurances for a passed
+ * Retrieves a copy of the maximum notification occurences for a passed
* notification id.
*
* @param id the notification id.
- * @return a copy of the maximum notification occurances or null when the
+ * @return a copy of the maximum notification occurences or null when the
* notification id is not registered.
*/
- public Long getNbOccurances(Integer id);
+ public Long getNbOccurences(Integer id);
/**
* Retrieves all timer notifications ids of the passed notification type.
_______________________________________________
Jboss-development mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/jboss-development