Hi It looks good (just came back from Europe) so I am a little bit jet-lagged. Could you do me a favour and check out the new ScheduleManager and its ScheduleProvider and see if you could adapt this CronSchedulable to the new Scheduler. I am going to deprecate the old Scheduler in favour of the ScheduleManager as soon as it is stable.
Have fun and thanx xxxxxxxxxxxxxxxxx Andreas Schaefer Senior Consultant JBoss Group, LLC xxxxxxxxxxxxxxxxx ----- Original Message ----- From: "Michael Bartmann" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Sent: Tuesday, August 13, 2002 3:53 PM Subject: [JBoss-dev] The Scheduler From Palma > Hi, > > included is the code for a "CronSchedulable" you can use to run jobs at > a specific time of day. > It's far from perfect but has helped me a lot so far. > Marc called me names at the Palma training for not giving this one to > the jboss community. > (He hasn't seen the code so far :-) > > Enjoy, > > Michael. > > -- > > "She won't live. But - then again - who does ?" -- Edward James Olmos in > 'Blade Runner' > ---------------------------------------------------------------------------- ---- > package de.j4production.base.time; > > import java.util.*; > import org.apache.log4j.*; > > /** > * A Schedulable to use from the JBoss Scheduler. > * It uses a format similar to the well known cron-command > * to specify the "exact" time to run. > * > * It is quite usefull to run cpu-intensive jobs in the > * midnight hours. > * > * To use it write a subclass which implements the > * performCron-method and instantiate it with an appropriate > * pattern. > * > * There are some limitations: > * > * 1) The advanceDay and nextTime functions are quite complex. > * You have to trust them or come up with something simpler. > * > * 2) The resolution you can get depends on the intervall in > * which the JBoss Scheduler calls this Schedulable. > * But: If the Scheduler failed to call in time, this is > * detected, and the performCron-method is immediatelly > * called the next time. > * > * 3) Not all cron formats are supported right now (NO RANGES!) > * > * 4) It could be better to port the time calculation to the > * Scheduler itself, so you can adjust the sleep-period > * to the circumstances. But you would need a priority-queue > * data structure to handle multiple Schedulables and adjust > * the sleep time for the first one in an efficient way. > * > * This code is under the LGPL-license. See the COPYING-file > * in the JBoss distribution. > * > * @author Michael Bartmann ([EMAIL PROTECTED]) > * @version 1.0 > * @copyright 4Production AG 2001 (www.4production.de) > */ > > public abstract class CronSchedulable > { > private static Category log = Category.getInstance(CronSchedulable.class); > > protected long lastRunMillis; > protected long nextRunMillis; > > private int[] minutes; > private int[] hours; > private int[] daysOfMonth; > private int[] months; > private int[] daysOfWeek; > > /** > * This constructor uses five separate Strings > * for the match patterns. > * > * Their format is similar to the well known cron command. > * See the main method below for an example. > * > * @param minutesPattern > * @param hourPattern > * @param dayPattern > * @param monthPattern > * @param weekdayPattern > */ > public CronSchedulable(String minutesPattern, > String hourPattern, > String dayPattern, > String monthPattern, > String weekdayPattern) > { > minutes = string2IntArray(minutesPattern,0,0,59,"minute"); > hours = string2IntArray(hourPattern,0,0,23,"hour"); > daysOfMonth = string2IntArray(dayPattern,0,1,31,"dayOfMonth"); > months = string2IntArray(monthPattern,-1,0,11,"month"); > daysOfWeek = string2IntArray(weekdayPattern,0,1,7,"dayOfWeek"); > lastRunMillis = System.currentTimeMillis(); > nextRunMillis = nextTime(lastRunMillis); > log.info(this+" started at: "+new Date(lastRunMillis)+", next run at approx.: "+new Date(nextRunMillis)); > } > > > /** > * helper function to parse the cron format > * @param xa > * @param min > * @param max > * @return > */ > private static int[] string2IntArray(String s, int offs, int min, int max, String fieldName) > { > if ((s==null)||(s.equals(""))||(s.equals("*"))) > { > return new int[0]; > } > StringTokenizer t = new StringTokenizer(s,"|",false); > int[] h = new int[t.countTokens()]; > for (int i=0; i<h.length; i++) > { > int x = Integer.parseInt(t.nextToken()) + offs; > if ((x<min)||(x>max)) > { > throw new IllegalArgumentException("parameter "+(x-offs)+" is out of range for field "+fieldName); > } > h[i] = x; > } > return h; > } > > /** > * helper function to parse the cron format > * @param xa > * @param min > * @param max > * @return > */ > private static boolean matches(int x, int[]xa, int min, int max) > { > if (xa.length==0) > { > return true; > } > else > { > for (int i=0;i<xa.length;i++) > { > if (xa[i]==x) > { > return true; > } > } > return false; > } > } > > /** > * helper function to parse the cron format > * @param xa > * @param min > * @param max > * @return > */ > private static boolean hasMoreMatches(int x, int[]xa, int min, int max) > { > if (xa.length==0) > { > return x<max; > } > else > { > for (int i=0;i<xa.length;i++) > { > if (xa[i]>x) > { > return true; > } > } > return false; > } > } > > /** > * helper function to parse the cron format > * @param xa > * @param min > * @param max > * @return > */ > private static int nextMatching(int x, int[]xa, int min, int max) > { > if (xa.length==0) > { > return x+1; > } > else > { > for (int i=0;i<xa.length;i++) > { > if (xa[i]>x) > { > return xa[i]; > } > } > return max; // should not happen!!! > } > } > > /** > * helper function to parse the cron format > * @param xa > * @param min > * @param max > * @return > */ > private static int first(int[]xa, int min, int max) > { > if (xa.length==0) > { > return min; > } > else > { > return xa[0]; > } > } > > /** > * Helper function to advance a calendar by one day. > * There should be a simpler way... > * @param gCal > */ > private void advanceDay(GregorianCalendar gCal) > { > do > { > gCal.add(GregorianCalendar.DATE,1); > } while (!( matches(gCal.get(GregorianCalendar.DAY_OF_MONTH),daysOfMonth,1,gCal.getActua lMaximum(GregorianCalendar.DAY_OF_MONTH)) > && matches(gCal.get(GregorianCalendar.MONTH),months,0,11) > && matches(gCal.get(GregorianCalendar.DAY_OF_WEEK),daysOfWeek,1,7) > )); > } > > /** > * Calculate the next time to run performCron, if we know that > * it was last run at the given argument. > * @param lastTime the last time performCron() was actually run. > * @return the next time to run performCron() > */ > private long nextTime(long lastTime) > { > GregorianCalendar gCal = new GregorianCalendar(); > gCal.setTime(new Date(lastTime)); > gCal.set(GregorianCalendar.SECOND,0); > gCal.set(GregorianCalendar.MILLISECOND,0); > if ( matches(gCal.get(GregorianCalendar.DAY_OF_WEEK),daysOfWeek,0,6) > && matches(gCal.get(GregorianCalendar.MONTH),months,0,11) > && matches(gCal.get(GregorianCalendar.DAY_OF_MONTH),daysOfMonth,0,31)) > { > // ok, so this is a matching day. > // if we find a next schedule for this day we're done, > // but if this is the last schedule for this day, we must advance > // to the next schedule on the next possible day. > if (matches(gCal.get(GregorianCalendar.HOUR_OF_DAY),hours,0,23)) > { > // everything matches up to the hour, we check the seconds > if (hasMoreMatches(gCal.get(GregorianCalendar.MINUTE),minutes,0,59)) > { > gCal.set(GregorianCalendar.MINUTE,nextMatching(gCal.get(GregorianCalendar.MI NUTE),minutes,0,59)); > // the rest of the fields remain intact > } > else > { > // oops, no more minute this hour > gCal.set(GregorianCalendar.MINUTE,first(minutes,0,59)); > // all other fields match, so we must advance hour > if (hasMoreMatches(gCal.get(GregorianCalendar.HOUR_OF_DAY),hours,0,23)) > { > gCal.set(GregorianCalendar.HOUR_OF_DAY,nextMatching(gCal.get(GregorianCalend ar.HOUR_OF_DAY),hours,0,23)); > // the rest of the fields remain intact > } > else > { > gCal.set(GregorianCalendar.HOUR_OF_DAY,first(hours,0,23)); > // all other fields match, so we must advance date > advanceDay(gCal); > } > } > } > else if (hasMoreMatches(gCal.get(GregorianCalendar.HOUR_OF_DAY),hours,0,23)) > { > gCal.set(GregorianCalendar.MINUTE,first(minutes,0,59)); > gCal.set(GregorianCalendar.HOUR_OF_DAY,nextMatching(gCal.get(GregorianCalend ar.HOUR_OF_DAY),hours,0,23)); > // the rest of the fields remain intact > } > else > { > gCal.set(GregorianCalendar.MINUTE,first(minutes,0,59)); > gCal.set(GregorianCalendar.HOUR_OF_DAY,first(hours,0,23)); > advanceDay(gCal); > } > } > else > { > gCal.set(GregorianCalendar.MINUTE,first(minutes,0,59)); > gCal.set(GregorianCalendar.HOUR_OF_DAY,first(hours,0,23)); > advanceDay(gCal); > } > return gCal.getTime().getTime(); > } > > /** > * This method is called by the JBoss build-in-scheduler. > * That scheduler must be configured to call this method as least as often > * to allow it to run performCron. > * @param pTimeOfCall > * @param pRemainingRepetitions > */ > public void perform(Date pTimeOfCall, long pRemainingRepetitions) > { > long now = System.currentTimeMillis(); > if (now>=nextRunMillis) > { > lastRunMillis = now; > nextRunMillis = nextTime(lastRunMillis); > log.info(this+" running at: "+new Date(lastRunMillis)); > performCron(); > log.info(this+" done, next run at approx.: "+new Date(nextRunMillis)); > } > } > > /** > * This method is called if the cron-arguments allow. > * Put your code here, when extending this class to do something useful. > */ > protected abstract void performCron(); > > > //---------- SNIP HERE TO GET RID OF DEBUGGING/EXAMPLE CODE --------------- > > > /** > * This method is only used by the example code in main. > */ > private void debug(long then) > { > long result = nextTime(then); > log.debug("next after "+new Date(then)+" is: "+new Date(result)); > } > > /** > * This main method constructs a simple CronSchedulable and calls > * the internal nextTime()-method. > * Normal debugging would last years :-) > */ > public static void main(String args[]) > { > CronSchedulable cron = new CronSchedulable("0|15|30|45","0|6|12|18","13","","6") > { > public void performCron() > { > log.debug("performCron " + new Date()); > } > }; > Random r = new Random(); > long now = System.currentTimeMillis(); > long then = now; > cron.debug(now); > for (int i=0; i<1000; i++) > { > long millisPerSecond = 1000; > long millisPerMinute = millisPerSecond * 60; > long millisPerHour = millisPerMinute * 60; > long millisPerDay = millisPerHour * 24; > long millisPerYear = millisPerDay * 366; > cron.debug(now+(r.nextLong()%(millisPerYear*40))); > } > } > > } ------------------------------------------------------- This sf.net email is sponsored by: Dice - The leading online job board for high-tech professionals. Search and apply for tech jobs today! http://seeker.dice.com/seeker.epl?rel_code=31 _______________________________________________ Jboss-development mailing list [EMAIL PROTECTED] https://lists.sourceforge.net/lists/listinfo/jboss-development