001: package net.suberic.pooka.thread;
002:
003: import net.suberic.pooka.FolderInfo;
004: import net.suberic.pooka.Pooka;
005: import net.suberic.util.thread.*;
006: import javax.mail.*;
007: import java.util.*;
008: import java.util.logging.*;
009: import java.awt.event.ActionEvent;
010:
011: /**
012: * This class polls the underlying Folder of a FolderInfo in order to make
013: * sure that the UnreadMessageCount is current and to make sure that the
014: * Folder stays open.
015: */
016: public class FolderTracker extends Thread {
017: private Vector mUpdateInfos = new Vector();
018: private CheckFolderAction mAction = new CheckFolderAction();
019: private long mTrackerNextUpdateTime = -1;
020: private boolean mStopped = false;
021: private Logger mLogger = null;
022:
023: /**
024: * Stores the information about the Folder to be updated.
025: */
026: private class UpdateInfo {
027: // the Folder for this UpdateInfo
028: FolderInfo folder;
029: // milliseconds between checks
030: long updateCheckMilliseconds;
031: // next time to update.
032: long nextFolderUpdate;
033: // whether or not we're waiting on this to complete.
034: boolean updateRunning = false;
035:
036: /**
037: * Creates a new UpdateInfo for FolderInfo <code>info</code>, with
038: * <code>updateCheck</code> milliseconds between checks.
039: */
040: public UpdateInfo(FolderInfo info, long updateCheck) {
041: folder = info;
042: updateCheckMilliseconds = updateCheck;
043: nextFolderUpdate = Calendar.getInstance().getTime()
044: .getTime()
045: + updateCheckMilliseconds;
046: }
047:
048: /**
049: * Updates the Folder.
050: */
051: public void update() {
052: getLogger().fine(
053: "creating update action for folder "
054: + folder.getFolderID());
055: updateRunning = true;
056: folder.getFolderThread().addToQueue(
057: getAction(),
058: new ActionEvent(this , 1, "folder-check - "
059: + folder.getFolderID()),
060: ActionThread.PRIORITY_LOW);
061: }
062:
063: /**
064: * Calculates the new next update time.
065: */
066: public void newUpdateTime() {
067: updateRunning = false;
068: nextFolderUpdate = Calendar.getInstance().getTime()
069: .getTime()
070: + updateCheckMilliseconds;
071: getLogger().finer(
072: "calculating new update time for "
073: + folder.getFolderID() + ": "
074: + nextFolderUpdate);
075: updateTrackerNextTime(nextFolderUpdate);
076: }
077:
078: /**
079: * Checks to see if we should run an update now.
080: */
081: public boolean shouldUpdate(long currentTime) {
082: return (!updateRunning && nextFolderUpdate <= currentTime);
083: }
084:
085: /**
086: * Returns the nextFolderUpdate time for this Info.
087: */
088: public long getNextFolderUpdate() {
089: return nextFolderUpdate;
090: }
091:
092: /**
093: * Returns the FolderInfo for this Info.
094: */
095: public FolderInfo getFolderInfo() {
096: return folder;
097: }
098:
099: /**
100: * Returns whether or not this is waiting on an outstaning update
101: * action.
102: */
103: public boolean isUpdateRunning() {
104: return updateRunning;
105: }
106:
107: } // end UpdateInfo.
108:
109: /**
110: * This creates a new FolderTracker from a FolderInfo object.
111: */
112: public FolderTracker() {
113: super ("Folder Tracker thread");
114: this .setPriority(1);
115: }
116:
117: // list management.
118:
119: /**
120: * This adds a FolderInfo to the FolderTracker.
121: */
122: public void addFolder(FolderInfo newFolder) {
123: if (newFolder == null)
124: return;
125:
126: getLogger().fine("adding folder " + newFolder.getFolderID());
127: long updateCheckMilliseconds;
128: String updateString = Pooka.getProperty(
129: "Pooka.updateCheckMilliseconds", "60000");
130:
131: if (newFolder.getParentStore() != null) {
132: updateString = Pooka.getProperty(newFolder
133: .getFolderProperty()
134: + ".updateCheckMilliseconds", Pooka.getProperty(
135: newFolder.getParentStore().getStoreProperty()
136: + ".updateCheckMilliseconds", Pooka
137: .getProperty(
138: "Pooka.updateCheckMilliseconds",
139: "60000")));
140: }
141: try {
142: updateCheckMilliseconds = Long.parseLong(updateString);
143: } catch (Exception e) {
144: updateCheckMilliseconds = 60000;
145: }
146:
147: UpdateInfo info = new UpdateInfo(newFolder,
148: updateCheckMilliseconds);
149: mUpdateInfos.add(info);
150: updateTrackerNextTime(info.getNextFolderUpdate());
151: }
152:
153: /**
154: * This removes a FolderInfo from the FolderTracker.
155: */
156: public void removeFolder(FolderInfo folder) {
157: if (folder == null)
158: return;
159:
160: getLogger().fine(
161: "removing folder " + folder.getFolderID()
162: + " from tracker.");
163:
164: for (int i = 0; i < mUpdateInfos.size(); i++)
165: if (((UpdateInfo) mUpdateInfos.elementAt(i)).folder == folder)
166: mUpdateInfos.removeElementAt(i);
167: }
168:
169: // end folder administration
170:
171: // next update time administration
172:
173: /**
174: * Adds a new update time. This assumes that there's already a
175: * valid update time, and that we're just making sure that the
176: * new time isn't sooner than that.
177: */
178: public synchronized void updateTrackerNextTime(long pTime) {
179: getLogger().finer(
180: "updating tracker next time with new value " + pTime
181: + ", old value " + mTrackerNextUpdateTime);
182: if (pTime < mTrackerNextUpdateTime) {
183: mTrackerNextUpdateTime = pTime;
184: getLogger()
185: .finer(
186: "new time is newer than old time; interrupting thread.");
187: interrupt();
188: }
189: }
190:
191: /**
192: * This returns the next update time. This assumes that the old
193: * update time is no longer valid, and that we should recalculate
194: * a new update time.
195: */
196: public synchronized long calculateNextUpdateTime(long currentTime) {
197: getLogger().finer("calculating next update time.");
198:
199: long nextTime = -1;
200: Iterator iter = mUpdateInfos.iterator();
201: while (iter.hasNext()) {
202: UpdateInfo current = (UpdateInfo) iter.next();
203: if (!current.isUpdateRunning()) {
204: if (nextTime == -1)
205: nextTime = current.getNextFolderUpdate();
206: else
207: nextTime = Math.min(nextTime, current
208: .getNextFolderUpdate());
209: }
210: }
211:
212: if (nextTime == -1)
213: nextTime = currentTime + 120000;
214:
215: mTrackerNextUpdateTime = nextTime;
216: getLogger().finer(
217: "new next update time: " + mTrackerNextUpdateTime);
218:
219: return mTrackerNextUpdateTime;
220: }
221:
222: // end update time admin
223:
224: // main method(s)
225:
226: /**
227: * This runs the thread, running checkFolder() every
228: * updateCheckMilliseconds until the thread is interrupted.
229: */
230: public void run() {
231: while (true && !mStopped) {
232: try {
233: getLogger().fine("running folder tracker update.");
234:
235: long currentTime = Calendar.getInstance().getTime()
236: .getTime();
237: updateFolders(currentTime);
238: long sleepTime = calculateNextUpdateTime(currentTime)
239: - currentTime;
240: if (sleepTime > 0) {
241: getLogger().finer(
242: "sleeping for " + sleepTime
243: + " milliseconds.");
244:
245: sleep(sleepTime);
246: } else {
247: getLogger().finer(
248: "sleep time is negative; not sleeping.");
249: }
250: } catch (InterruptedException ie) {
251: // on interrupt, just continue.
252: getLogger().finer("caught InterruptedException.");
253: }
254: }
255:
256: getLogger().fine("Stopped. Shutting down Folder Tracker.");
257: }
258:
259: /**
260: * Goes through the list of folders and updates the ones that are
261: * due for an update.
262: */
263: public void updateFolders(long currentTime) {
264: for (int i = 0; i < mUpdateInfos.size(); i++) {
265: UpdateInfo info = (UpdateInfo) mUpdateInfos.elementAt(i);
266: if (info.shouldUpdate(currentTime))
267: info.update();
268: }
269: }
270:
271: // end main methods
272:
273: // thread control
274:
275: /**
276: * Singals that the tracker thread should stop.
277: */
278: public void setStopped(boolean pStopped) {
279: mStopped = pStopped;
280: getLogger()
281: .fine("setting FolderTracker stopped to " + mStopped);
282: if (mStopped == true)
283: interrupt();
284: }
285:
286: // end thread control
287:
288: // action section
289:
290: /**
291: * This returns the action to run when it's time to update the folder.
292: */
293: public javax.swing.Action getAction() {
294: return mAction;
295: }
296:
297: /**
298: * The Action that's put in the queue for checking the folder
299: * status.
300: */
301: public class CheckFolderAction extends javax.swing.AbstractAction {
302: public CheckFolderAction() {
303: super ("folder-check");
304: }
305:
306: public void actionPerformed(java.awt.event.ActionEvent e) {
307: UpdateInfo info = (UpdateInfo) e.getSource();
308: try {
309: getLogger().fine(
310: "running checkFolder on "
311: + info.getFolderInfo().getFolderID());
312: info.getFolderInfo().checkFolder();
313: } catch (MessagingException me) {
314: // ignore; only show if we're debugging.
315: if (getLogger().isLoggable(Level.FINE)) {
316: getLogger()
317: .fine(
318: "caught exception checking folder "
319: + info.getFolderInfo()
320: .getFolderID()
321: + ": " + me);
322: me.printStackTrace();
323: }
324: } finally {
325: info.newUpdateTime();
326: }
327: }
328: }
329:
330: // end action section
331:
332: // logging
333:
334: /**
335: * Gets the Logger for this class.
336: */
337: public Logger getLogger() {
338: if (mLogger == null) {
339: mLogger = java.util.logging.Logger
340: .getLogger("Pooka.debug.folderTracker");
341: }
342:
343: return mLogger;
344: }
345: }
|