001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: package com.jcorporate.expresso.services.crontab;
066:
067: import org.apache.log4j.Logger;
068:
069: import java.util.Date;
070: import java.util.Iterator;
071: import java.util.LinkedList;
072: import java.util.List;
073: import java.util.SortedSet;
074: import java.util.TreeSet;
075:
076: /**
077: * This is the main API controller class that deals with the various crontab
078: * entries.
079: *
080: * @author Mike Dubman
081: */
082: public class Crontab {
083:
084: /**
085: * The thread that manages the crontab entries. It does so by simply ringing
086: * this particular class that a cron is ready to run.
087: */
088: protected CronDaemon waiter;
089:
090: /**
091: * The queue waiting for execution. Sorting in the set is determined
092: * by the next execution time.
093: */
094: protected SortedSet queue;
095:
096: /**
097: * The log4j logger.
098: */
099: private static final Logger log = Logger.getLogger(Crontab.class);
100:
101: /**
102: * Construct the Crontab thread
103: *
104: * @param isDaemon set to true if the crontab should be a daemon thread
105: * @param threadName the name of the thread
106: */
107: public Crontab(boolean isDaemon, String threadName) {
108: queue = (SortedSet) new TreeSet();
109: waiter = new CronDaemon(this , isDaemon, threadName);
110: }
111:
112: /**
113: * Default constructor
114: */
115: public Crontab() {
116: this (true, "Crontab");
117: }
118:
119: /**
120: * Add a crontab entry
121: *
122: * @param date the date for the crontab entry to execute
123: * @param listener the listener interface
124: * @return CrontabEntry representing what was just constructed
125: * @throws CronException upon error constructing the CrontabEntry
126: */
127: public synchronized CrontabEntry addCrontabEntry(Date date,
128: CrontabListenerI listener) throws CronException {
129: CrontabEntry entry = new CrontabEntry(date, listener);
130: addCrontabEntry(entry);
131:
132: return entry;
133: }
134:
135: /**
136: * Add a crontab entry
137: *
138: * @param delay the delay in(?) before executing the crontab
139: * @param isRepetitive true if the crontab is repetitive, ie should it execute
140: * every X many seconds
141: * @param listener the listener to get notified when a crontab executes
142: * @return CrontabEntry
143: * @throws CronException upon error creating the crontab entry
144: */
145: public synchronized CrontabEntry addCrontabEntry(int delay,
146: boolean isRepetitive, CrontabListenerI listener)
147: throws CronException {
148: CrontabEntry entry = new CrontabEntry(delay, isRepetitive,
149: listener);
150: addCrontabEntry(entry);
151:
152: return entry;
153: }
154:
155: /**
156: * Adds a crontab to execute based upon various Cron-like parameters as
157: * numbers
158: *
159: * @param minute execute every X minutes
160: * @param hour execute every X hours
161: * @param dayOfMonth execute every X days of the month
162: * @param month execute every X days of the month
163: * @param dayOfWeek execute every specified day of the week
164: * @param year execute every X specified years
165: * @param listener the listener to get notified when the crontab's time is
166: * due
167: * @return Crontab Entry
168: * @throws CronException upon construction error
169: */
170: public synchronized CrontabEntry addCrontabEntry(int minute,
171: int hour, int dayOfMonth, int month, int dayOfWeek,
172: int year, CrontabListenerI listener) throws CronException {
173: CrontabEntry entry = new CrontabEntry(minute, hour, dayOfMonth,
174: month, dayOfWeek, year, listener);
175: addCrontabEntry(entry);
176:
177: return entry;
178: }
179:
180: /**
181: * Adds a crontab to execute based upon various Cron-like parameters as
182: * numbers
183: *
184: * @param minute execute every X minutes
185: * @param hour execute every X hours
186: * @param dayOfMonth execute every X days of the month
187: * @param month execute every X days of the month
188: * @param dayOfWeek execute every specified day of the week
189: * @param year execute every X specified years
190: * @param label Useful label for possible debugging purposes
191: * @param listener the listener to get notified when the crontab's time is
192: * due
193: * @return Crontab Entry
194: * @throws CronException upon construction error
195: */
196: public synchronized CrontabEntry addCrontabEntry(int minute,
197: int hour, int dayOfMonth, int month, int dayOfWeek,
198: int year, String label, CrontabListenerI listener)
199: throws CronException {
200: CrontabEntry entry = new CrontabEntry(minute, hour, dayOfMonth,
201: month, dayOfWeek, year, label, listener);
202: addCrontabEntry(entry);
203:
204: return entry;
205: }
206:
207: /**
208: * Adds a crontab to execute based upon various Cron-like parameters as
209: * numbers
210: *
211: * @param minute execute every X minutes
212: * @param hour execute every X hours
213: * @param dayOfMonth execute every X days of the month
214: * @param month execute every X days of the month
215: * @param dayOfWeek execute every specified day of the week
216: * @param year execute every X specified years
217: * @param label Useful label for possible debugging purposes
218: * @param listener the listener to get notified when the crontab's time is
219: * due
220: * @param jobNumber Job Number for this CrontabEntry's associated JobQueue entry
221: * @return Crontab Entry
222: * @throws CronException upon construction error
223: */
224: public synchronized CrontabEntry addCrontabEntry(int minute,
225: int hour, int dayOfMonth, int month, int dayOfWeek,
226: int year, String label, CrontabListenerI listener,
227: String jobNumber) throws CronException {
228: CrontabEntry entry = new CrontabEntry(minute, hour, dayOfMonth,
229: month, dayOfWeek, year, label, listener);
230: entry.setJobNumber(jobNumber);
231: addCrontabEntry(entry);
232:
233: return entry;
234: }
235:
236: /**
237: * Add a constructed Crontab entry to the Crontab.
238: *
239: * @param entry the entry to add
240: */
241: public synchronized void addCrontabEntry(CrontabEntry entry)
242: throws CronException {
243: if (log.isDebugEnabled()) {
244: log.debug("Adding Crontab Entry: " + entry.toString());
245: }
246:
247: //
248: //First check for any already existing crontab entries We have
249: //to do it the slow iterative way because comparable is ALWAYS unque even
250: //if it's the same crontab. We have to rely on equals
251: //
252: for (Iterator i = queue.iterator(); i.hasNext();) {
253: CrontabEntry test = (CrontabEntry) i.next();
254: if (test.equals(entry)) {
255: if (log.isInfoEnabled()) {
256: log.info("Crontab entry: " + entry
257: + " already exists in crontab. Skipping");
258: }
259: return;
260: }
261: }
262:
263: queue.add(entry);
264: waiter.update(((CrontabEntry) queue.first()).alarmTime);
265: }
266:
267: /**
268: * Remove a given crontab entry from the crontab
269: *
270: * @param entry the entry to remove
271: * @return boolean
272: */
273: public synchronized boolean removeCrontabEntry(CrontabEntry entry) {
274:
275: //
276: //First check for any already existing crontab entries We have
277: //to do it the slow iterative way because comparable is ALWAYS unque even
278: //if it's the same crontab. We have to rely on equals
279: //
280: for (Iterator i = queue.iterator(); i.hasNext();) {
281: CrontabEntry test = (CrontabEntry) i.next();
282: if (test.equals(entry)) {
283: if (log.isInfoEnabled()) {
284: log.info("Crontab entry: " + entry
285: + " already exists in crontab. Skipping");
286: }
287: queue.remove(test);
288: if (queue.size() > 0) {
289: waiter
290: .update(((CrontabEntry) queue.first()).alarmTime);
291: }
292: return true;
293: }
294: }
295:
296: return false;
297: }
298:
299: /**
300: * Remove all existing crontabs from the crontab entry.
301: */
302: public synchronized void removeAllCrontabEntries() {
303: if (waiter != null) {
304: waiter.stop();
305: }
306:
307: waiter = null;
308: queue.clear();
309: }
310:
311: /**
312: * Checks if a given crontab entry is sitting in the queue waiting for execution
313: *
314: * @param oneEntry a given entry.
315: * @return true if the given entry exists in the queue
316: */
317: public synchronized boolean containsCrontabEntry(
318: CrontabEntry oneEntry) {
319: return queue.contains(oneEntry);
320: }
321:
322: /**
323: * Retrieve a list of all items that are part of the queue.
324: *
325: * @return a List of CrontabEntry objects
326: */
327: public synchronized List getAllEntries() {
328: final LinkedList result = new LinkedList();
329: Iterator iterator = queue.iterator();
330:
331: while (iterator.hasNext()) {
332: result.add(iterator.next());
333: }
334:
335: return result;
336: }
337:
338: /**
339: * When this is called, we execute the given crontab
340: */
341: protected synchronized void notifyListeners() {
342:
343: // if the queue is empty, there's nothing to do
344: if (queue.isEmpty()) {
345: log
346: .debug("Execution queue is empty, exiting notifyListeners()");
347: return;
348: } // if
349:
350: // Removes this alarm and notifies the listener
351: CrontabEntry entry = (CrontabEntry) queue.first();
352: queue.remove(entry);
353:
354: try {
355: if (log.isDebugEnabled()) {
356: log
357: .debug("Handling crontab entry: "
358: + entry.toString());
359: }
360:
361: entry.listener.handleCrontabEntry(entry);
362: } catch (Throwable e) {
363: log.error("Error handling crontab entry", e);
364: }
365: // Reactivates the alarm if it is repetitive
366: if (entry.isRepetitive) {
367: if (log.isDebugEnabled()) {
368: log
369: .debug("Re-adding repetitive crontab back to queue: "
370: + entry.toString());
371: }
372: entry.updateEntryTime();
373: queue.add(entry);
374: } else {
375: if (log.isDebugEnabled()) {
376: log.debug("Finished non-repetitive cron: "
377: + entry.toString());
378: }
379: }
380:
381: // Notifies the CronDaemon thread for the next alarm
382: if (!queue.isEmpty()) {
383: long alarmTime = ((CrontabEntry) queue.first()).alarmTime;
384:
385: if (alarmTime - System.currentTimeMillis() < 1000) {
386: notifyListeners();
387: } else {
388: waiter.restart(alarmTime);
389: }
390: }
391:
392: if (log.isDebugEnabled()) {
393: log
394: .debug("Execution queue is empty, exiting notifyListeners()");
395:
396: }
397: }
398:
399: /**
400: * Clearing the cron thread if the crontab is killed
401: */
402: public void finalize() {
403: if (waiter != null) {
404: waiter.stop();
405: }
406: }
407: }
|