From 05dc89676950e6c38935cd806aabebb718cca15c Mon Sep 17 00:00:00 2001
From: zhouping
+ * An interface to be implemented by objects that define spaces of time during
+ * which an associated
+ * Set a new base calendar or remove the existing one.
+ *
+ * Get the base calendar. Will be null, if not set.
+ *
+ * Determine whether the given time (in milliseconds) is 'included' by the
+ * Calendar.
+ *
+ * Determine the next time (in milliseconds) that is 'included' by the
+ * Calendar after the given time.
+ *
+ * Return the description given to the
+ * Set a description for the
+ * An exception that is thrown to indicate that there has been a critical
+ * failure within the scheduler's core services (such as loss of database
+ * connectivity).
+ *
+ * Create a
+ * Cron expressions are comprised of 6 required fields and one optional field
+ * separated by white space. The fields respectively are described as follows:
+ *
+ *
+ * The '*' character is used to specify all values. For example, "*"
+ * in the minute field means "every minute".
+ *
+ * The '?' character is allowed for the day-of-month and day-of-week fields. It
+ * is used to specify 'no specific value'. This is useful when you need to
+ * specify something in one of the two fields, but not the other.
+ *
+ * The '-' character is used to specify ranges For example "10-12" in
+ * the hour field means "the hours 10, 11 and 12".
+ *
+ * The ',' character is used to specify additional values. For example
+ * "MON,WED,FRI" in the day-of-week field means "the days Monday,
+ * Wednesday, and Friday".
+ *
+ * The '/' character is used to specify increments. For example "0/15"
+ * in the seconds field means "the seconds 0, 15, 30, and 45". And
+ * "5/15" in the seconds field means "the seconds 5, 20, 35, and
+ * 50". Specifying '*' before the '/' is equivalent to specifying 0 is
+ * the value to start with. Essentially, for each field in the expression, there
+ * is a set of numbers that can be turned on or off. For seconds and minutes,
+ * the numbers range from 0 to 59. For hours 0 to 23, for days of the month 0 to
+ * 31, and for months 1 to 12. The "/" character simply helps you turn
+ * on every "nth" value in the given set. Thus "7/6" in the
+ * month field only turns on month "7", it does NOT mean every 6th
+ * month, please note that subtlety.
+ *
+ * The 'L' character is allowed for the day-of-month and day-of-week fields.
+ * This character is short-hand for "last", but it has different
+ * meaning in each of the two fields. For example, the value "L" in
+ * the day-of-month field means "the last day of the month" - day 31
+ * for January, day 28 for February on non-leap years. If used in the
+ * day-of-week field by itself, it simply means "7" or
+ * "SAT". But if used in the day-of-week field after another value, it
+ * means "the last xxx day of the month" - for example "6L"
+ * means "the last friday of the month". When using the 'L' option, it
+ * is important not to specify lists, or ranges of values, as you'll get
+ * confusing results.
+ *
+ * The 'W' character is allowed for the day-of-month field. This character
+ * is used to specify the weekday (Monday-Friday) nearest the given day. As an
+ * example, if you were to specify "15W" as the value for the
+ * day-of-month field, the meaning is: "the nearest weekday to the 15th of
+ * the month". So if the 15th is a Saturday, the trigger will fire on
+ * Friday the 14th. If the 15th is a Sunday, the trigger will fire on Monday the
+ * 16th. If the 15th is a Tuesday, then it will fire on Tuesday the 15th.
+ * However if you specify "1W" as the value for day-of-month, and the
+ * 1st is a Saturday, the trigger will fire on Monday the 3rd, as it will not
+ * 'jump' over the boundary of a month's days. The 'W' character can only be
+ * specified when the day-of-month is a single day, not a range or list of days.
+ *
+ * The 'L' and 'W' characters can also be combined for the day-of-month
+ * expression to yield 'LW', which translates to "last weekday of the
+ * month".
+ *
+ * The '#' character is allowed for the day-of-week field. This character is
+ * used to specify "the nth" XXX day of the month. For example, the
+ * value of "6#3" in the day-of-week field means the third Friday of
+ * the month (day 6 = Friday and "#3" = the 3rd one in the month).
+ * Other examples: "2#1" = the first Monday of the month and
+ * "4#5" = the fifth Wednesday of the month. Note that if you specify
+ * "#5" and there is not 5 of the given day-of-week in the month, then
+ * no firing will occur that month. If the '#' character is used, there can
+ * only be one expression in the day-of-week field ("3#1,6#3" is
+ * not valid, since there are two expressions).
+ *
+ *
+ *
+ * The legal characters and the names of months and days of the week are not
+ * case sensitive.
+ *
+ *
+ * NOTES:
+ *
+2.源码地址https://cloud.finedevelop.com/projects/PF/repos/thirdtools/browse,
+3.相比源码调整了默认的连接池为druid,以前是dbcp
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/Calendar.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/Calendar.java
new file mode 100644
index 000000000..f0a97e68f
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/Calendar.java
@@ -0,0 +1,111 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+
+
+package com.fr.third.org.quartz;
+
+/**
+ * {@link Trigger}
may fire. Calendars do not
+ * define actual fire times, but rather are used to limit a Trigger
+ * from firing on its normal schedule if necessary. Most Calendars include all
+ * times by default and allow the user to specify times to exclude. As such, it
+ * is often useful to think of Calendars as being used to exclude a block
+ * of time — as opposed to include a block of time. (i.e. the
+ * schedule "fire every five minutes except on Sundays" could be
+ * implemented with a SimpleTrigger
and a
+ * WeeklyCalendar
which excludes Sundays)
+ * Calendar
instance by
+ * its creator (if any).
+ * Calendar
instance - may be
+ * useful for remembering/displaying the purpose of the calendar, though
+ * the description has no meaning to Quartz.
+ * CriticalSchedulerException
with the given message.
+ *
+ *
+ *
+ *
+ * Field Name
+ *
+ * Allowed Values
+ *
+ * Allowed Special Characters
+ *
+ *
+ *
+ * Seconds
+ *
+ * 0-59
+ *
+ * , - * /
+ *
+ *
+ * Minutes
+ *
+ * 0-59
+ *
+ * , - * /
+ *
+ *
+ * Hours
+ *
+ * 0-23
+ *
+ * , - * /
+ *
+ *
+ * Day-of-month
+ *
+ * 1-31
+ *
+ * , - * ? / L W
+ *
+ *
+ * Month
+ *
+ * 1-12 or JAN-DEC
+ *
+ * , - * /
+ *
+ *
+ * Day-of-Week
+ *
+ * 1-7 or SUN-SAT
+ *
+ * , - * ? / L #
+ *
+ *
+ * Year (Optional)
+ *
+ * empty, 1970-2099
+ *
+ * , - * /
+ *
+ *
CronExpression
based on the specified
+ * parameter.
+ *
+ * @param cronExpression String representation of the cron expression the
+ * new object should represent
+ * @throws java.text.ParseException
+ * if the string expression cannot be parsed into a valid
+ * CronExpression
+ */
+ public CronExpression(String cronExpression) throws ParseException {
+ if (cronExpression == null) {
+ throw new IllegalArgumentException("cronExpression cannot be null");
+ }
+
+ this.cronExpression = cronExpression.toUpperCase(Locale.US);
+
+ buildExpression(this.cronExpression);
+ }
+
+ /**
+ * Indicates whether the given date satisfies the cron expression. Note that
+ * milliseconds are ignored, so two Dates falling on different milliseconds
+ * of the same second will always have the same result here.
+ *
+ * @param date the date to evaluate
+ * @return a boolean indicating whether the given date satisfies the cron
+ * expression
+ */
+ public boolean isSatisfiedBy(Date date) {
+ Calendar testDateCal = Calendar.getInstance(getTimeZone());
+ testDateCal.setTime(date);
+ testDateCal.set(Calendar.MILLISECOND, 0);
+ Date originalDate = testDateCal.getTime();
+
+ testDateCal.add(Calendar.SECOND, -1);
+
+ Date timeAfter = getTimeAfter(testDateCal.getTime());
+
+ return ((timeAfter != null) && (timeAfter.equals(originalDate)));
+ }
+
+ /**
+ * Returns the next date/time after the given date/time which
+ * satisfies the cron expression.
+ *
+ * @param date the date/time at which to begin the search for the next valid
+ * date/time
+ * @return the next valid date/time
+ */
+ public Date getNextValidTimeAfter(Date date) {
+ return getTimeAfter(date);
+ }
+
+ /**
+ * Returns the next date/time after the given date/time which does
+ * not satisfy the expression
+ *
+ * @param date the date/time at which to begin the search for the next
+ * invalid date/time
+ * @return the next valid date/time
+ */
+ public Date getNextInvalidTimeAfter(Date date) {
+ long difference = 1000;
+
+ //move back to the nearest second so differences will be accurate
+ Calendar adjustCal = Calendar.getInstance(getTimeZone());
+ adjustCal.setTime(date);
+ adjustCal.set(Calendar.MILLISECOND, 0);
+ Date lastDate = adjustCal.getTime();
+
+ Date newDate = null;
+
+ //TODO: (QUARTZ-481) IMPROVE THIS! The following is a BAD solution to this problem. Performance will be very bad here, depending on the cron expression. It is, however A solution.
+
+ //keep getting the next included time until it's farther than one second
+ // apart. At that point, lastDate is the last valid fire time. We return
+ // the second immediately following it.
+ while (difference == 1000) {
+ newDate = getTimeAfter(lastDate);
+
+ difference = newDate.getTime() - lastDate.getTime();
+
+ if (difference == 1000) {
+ lastDate = newDate;
+ }
+ }
+
+ return new Date(lastDate.getTime() + 1000);
+ }
+
+ /**
+ * Returns the time zone for which this CronExpression
+ * will be resolved.
+ */
+ public TimeZone getTimeZone() {
+ if (timeZone == null) {
+ timeZone = TimeZone.getDefault();
+ }
+
+ return timeZone;
+ }
+
+ /**
+ * Sets the time zone for which this CronExpression
+ * will be resolved.
+ */
+ public void setTimeZone(TimeZone timeZone) {
+ this.timeZone = timeZone;
+ }
+
+ /**
+ * Returns the string representation of the CronExpression
+ *
+ * @return a string representation of the CronExpression
+ */
+ public String toString() {
+ return cronExpression;
+ }
+
+ /**
+ * Indicates whether the specified cron expression can be parsed into a
+ * valid cron expression
+ *
+ * @param cronExpression the expression to evaluate
+ * @return a boolean indicating whether the given expression is a valid cron
+ * expression
+ */
+ public static boolean isValidExpression(String cronExpression) {
+
+ try {
+ new CronExpression(cronExpression);
+ } catch (ParseException pe) {
+ return false;
+ }
+
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Expression Parsing Functions
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected void buildExpression(String expression) throws ParseException {
+ expressionParsed = true;
+
+ try {
+
+ if (seconds == null) {
+ seconds = new TreeSet();
+ }
+ if (minutes == null) {
+ minutes = new TreeSet();
+ }
+ if (hours == null) {
+ hours = new TreeSet();
+ }
+ if (daysOfMonth == null) {
+ daysOfMonth = new TreeSet();
+ }
+ if (months == null) {
+ months = new TreeSet();
+ }
+ if (daysOfWeek == null) {
+ daysOfWeek = new TreeSet();
+ }
+ if (years == null) {
+ years = new TreeSet();
+ }
+
+ int exprOn = SECOND;
+
+ StringTokenizer exprsTok = new StringTokenizer(expression, " \t",
+ false);
+
+ while (exprsTok.hasMoreTokens() && exprOn <= YEAR) {
+ String expr = exprsTok.nextToken().trim();
+
+ // throw an exception if L is used with other days of the month
+ if(exprOn == DAY_OF_MONTH && expr.indexOf('L') != -1 && expr.length() > 1 && expr.indexOf(",") >= 0) {
+ throw new ParseException("Support for specifying 'L' and 'LW' with other days of the month is not implemented", -1);
+ }
+ // throw an exception if L is used with other days of the week
+ if(exprOn == DAY_OF_WEEK && expr.indexOf('L') != -1 && expr.length() > 1 && expr.indexOf(",") >= 0) {
+ throw new ParseException("Support for specifying 'L' with other days of the week is not implemented", -1);
+ }
+
+ StringTokenizer vTok = new StringTokenizer(expr, ",");
+ while (vTok.hasMoreTokens()) {
+ String v = vTok.nextToken();
+ storeExpressionVals(0, v, exprOn);
+ }
+
+ exprOn++;
+ }
+
+ if (exprOn <= DAY_OF_WEEK) {
+ throw new ParseException("Unexpected end of expression.",
+ expression.length());
+ }
+
+ if (exprOn <= YEAR) {
+ storeExpressionVals(0, "*", YEAR);
+ }
+
+ TreeSet dow = getSet(DAY_OF_WEEK);
+ TreeSet dom = getSet(DAY_OF_MONTH);
+
+ // Copying the logic from the UnsupportedOperationException below
+ boolean dayOfMSpec = !dom.contains(NO_SPEC);
+ boolean dayOfWSpec = !dow.contains(NO_SPEC);
+
+ if (dayOfMSpec && !dayOfWSpec) {
+ // skip
+ } else if (dayOfWSpec && !dayOfMSpec) {
+ // skip
+ } else {
+ throw new ParseException(
+ "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.", 0);
+ }
+ } catch (ParseException pe) {
+ throw pe;
+ } catch (Exception e) {
+ throw new ParseException("Illegal cron expression format ("
+ + e.toString() + ")", 0);
+ }
+ }
+
+ protected int storeExpressionVals(int pos, String s, int type)
+ throws ParseException {
+
+ int incr = 0;
+ int i = skipWhiteSpace(pos, s);
+ if (i >= s.length()) {
+ return i;
+ }
+ char c = s.charAt(i);
+ if ((c >= 'A') && (c <= 'Z') && (!s.equals("L")) && (!s.equals("LW"))) {
+ String sub = s.substring(i, i + 3);
+ int sval = -1;
+ int eval = -1;
+ if (type == MONTH) {
+ sval = getMonthNumber(sub) + 1;
+ if (sval <= 0) {
+ throw new ParseException("Invalid Month value: '" + sub + "'", i);
+ }
+ if (s.length() > i + 3) {
+ c = s.charAt(i + 3);
+ if (c == '-') {
+ i += 4;
+ sub = s.substring(i, i + 3);
+ eval = getMonthNumber(sub) + 1;
+ if (eval <= 0) {
+ throw new ParseException("Invalid Month value: '" + sub + "'", i);
+ }
+ }
+ }
+ } else if (type == DAY_OF_WEEK) {
+ sval = getDayOfWeekNumber(sub);
+ if (sval < 0) {
+ throw new ParseException("Invalid Day-of-Week value: '"
+ + sub + "'", i);
+ }
+ if (s.length() > i + 3) {
+ c = s.charAt(i + 3);
+ if (c == '-') {
+ i += 4;
+ sub = s.substring(i, i + 3);
+ eval = getDayOfWeekNumber(sub);
+ if (eval < 0) {
+ throw new ParseException(
+ "Invalid Day-of-Week value: '" + sub
+ + "'", i);
+ }
+ } else if (c == '#') {
+ try {
+ i += 4;
+ nthdayOfWeek = Integer.parseInt(s.substring(i));
+ if (nthdayOfWeek < 1 || nthdayOfWeek > 5) {
+ throw new Exception();
+ }
+ } catch (Exception e) {
+ throw new ParseException(
+ "A numeric value between 1 and 5 must follow the '#' option",
+ i);
+ }
+ } else if (c == 'L') {
+ lastdayOfWeek = true;
+ i++;
+ }
+ }
+
+ } else {
+ throw new ParseException(
+ "Illegal characters for this position: '" + sub + "'",
+ i);
+ }
+ if (eval != -1) {
+ incr = 1;
+ }
+ addToSet(sval, eval, incr, type);
+ return (i + 3);
+ }
+
+ if (c == '?') {
+ i++;
+ if ((i + 1) < s.length()
+ && (s.charAt(i) != ' ' && s.charAt(i + 1) != '\t')) {
+ throw new ParseException("Illegal character after '?': "
+ + s.charAt(i), i);
+ }
+ if (type != DAY_OF_WEEK && type != DAY_OF_MONTH) {
+ throw new ParseException(
+ "'?' can only be specfied for Day-of-Month or Day-of-Week.",
+ i);
+ }
+ if (type == DAY_OF_WEEK && !lastdayOfMonth) {
+ int val = ((Integer) daysOfMonth.last()).intValue();
+ if (val == NO_SPEC_INT) {
+ throw new ParseException(
+ "'?' can only be specfied for Day-of-Month -OR- Day-of-Week.",
+ i);
+ }
+ }
+
+ addToSet(NO_SPEC_INT, -1, 0, type);
+ return i;
+ }
+
+ if (c == '*' || c == '/') {
+ if (c == '*' && (i + 1) >= s.length()) {
+ addToSet(ALL_SPEC_INT, -1, incr, type);
+ return i + 1;
+ } else if (c == '/'
+ && ((i + 1) >= s.length() || s.charAt(i + 1) == ' ' || s
+ .charAt(i + 1) == '\t')) {
+ throw new ParseException("'/' must be followed by an integer.", i);
+ } else if (c == '*') {
+ i++;
+ }
+ c = s.charAt(i);
+ if (c == '/') { // is an increment specified?
+ i++;
+ if (i >= s.length()) {
+ throw new ParseException("Unexpected end of string.", i);
+ }
+
+ incr = getNumericValue(s, i);
+
+ i++;
+ if (incr > 10) {
+ i++;
+ }
+ if (incr > 59 && (type == SECOND || type == MINUTE)) {
+ throw new ParseException("Increment > 60 : " + incr, i);
+ } else if (incr > 23 && (type == HOUR)) {
+ throw new ParseException("Increment > 24 : " + incr, i);
+ } else if (incr > 31 && (type == DAY_OF_MONTH)) {
+ throw new ParseException("Increment > 31 : " + incr, i);
+ } else if (incr > 7 && (type == DAY_OF_WEEK)) {
+ throw new ParseException("Increment > 7 : " + incr, i);
+ } else if (incr > 12 && (type == MONTH)) {
+ throw new ParseException("Increment > 12 : " + incr, i);
+ }
+ } else {
+ incr = 1;
+ }
+
+ addToSet(ALL_SPEC_INT, -1, incr, type);
+ return i;
+ } else if (c == 'L') {
+ i++;
+ if (type == DAY_OF_MONTH) {
+ lastdayOfMonth = true;
+ }
+ if (type == DAY_OF_WEEK) {
+ addToSet(7, 7, 0, type);
+ }
+ if(type == DAY_OF_MONTH && s.length() > i) {
+ c = s.charAt(i);
+ if(c == 'W') {
+ nearestWeekday = true;
+ i++;
+ }
+ }
+ return i;
+ } else if (c >= '0' && c <= '9') {
+ int val = Integer.parseInt(String.valueOf(c));
+ i++;
+ if (i >= s.length()) {
+ addToSet(val, -1, -1, type);
+ } else {
+ c = s.charAt(i);
+ if (c >= '0' && c <= '9') {
+ ValueSet vs = getValue(val, s, i);
+ val = vs.value;
+ i = vs.pos;
+ }
+ i = checkNext(i, s, val, type);
+ return i;
+ }
+ } else {
+ throw new ParseException("Unexpected character: " + c, i);
+ }
+
+ return i;
+ }
+
+ protected int checkNext(int pos, String s, int val, int type)
+ throws ParseException {
+
+ int end = -1;
+ int i = pos;
+
+ if (i >= s.length()) {
+ addToSet(val, end, -1, type);
+ return i;
+ }
+
+ char c = s.charAt(pos);
+
+ if (c == 'L') {
+ if (type == DAY_OF_WEEK) {
+ lastdayOfWeek = true;
+ } else {
+ throw new ParseException("'L' option is not valid here. (pos=" + i + ")", i);
+ }
+ TreeSet set = getSet(type);
+ set.add(new Integer(val));
+ i++;
+ return i;
+ }
+
+ if (c == 'W') {
+ if (type == DAY_OF_MONTH) {
+ nearestWeekday = true;
+ } else {
+ throw new ParseException("'W' option is not valid here. (pos=" + i + ")", i);
+ }
+ TreeSet set = getSet(type);
+ set.add(new Integer(val));
+ i++;
+ return i;
+ }
+
+ if (c == '#') {
+ if (type != DAY_OF_WEEK) {
+ throw new ParseException("'#' option is not valid here. (pos=" + i + ")", i);
+ }
+ i++;
+ try {
+ nthdayOfWeek = Integer.parseInt(s.substring(i));
+ if (nthdayOfWeek < 1 || nthdayOfWeek > 5) {
+ throw new Exception();
+ }
+ } catch (Exception e) {
+ throw new ParseException(
+ "A numeric value between 1 and 5 must follow the '#' option",
+ i);
+ }
+
+ TreeSet set = getSet(type);
+ set.add(new Integer(val));
+ i++;
+ return i;
+ }
+
+ if (c == '-') {
+ i++;
+ c = s.charAt(i);
+ int v = Integer.parseInt(String.valueOf(c));
+ end = v;
+ i++;
+ if (i >= s.length()) {
+ addToSet(val, end, 1, type);
+ return i;
+ }
+ c = s.charAt(i);
+ if (c >= '0' && c <= '9') {
+ ValueSet vs = getValue(v, s, i);
+ int v1 = vs.value;
+ end = v1;
+ i = vs.pos;
+ }
+ if (i < s.length() && ((c = s.charAt(i)) == '/')) {
+ i++;
+ c = s.charAt(i);
+ int v2 = Integer.parseInt(String.valueOf(c));
+ i++;
+ if (i >= s.length()) {
+ addToSet(val, end, v2, type);
+ return i;
+ }
+ c = s.charAt(i);
+ if (c >= '0' && c <= '9') {
+ ValueSet vs = getValue(v2, s, i);
+ int v3 = vs.value;
+ addToSet(val, end, v3, type);
+ i = vs.pos;
+ return i;
+ } else {
+ addToSet(val, end, v2, type);
+ return i;
+ }
+ } else {
+ addToSet(val, end, 1, type);
+ return i;
+ }
+ }
+
+ if (c == '/') {
+ i++;
+ c = s.charAt(i);
+ int v2 = Integer.parseInt(String.valueOf(c));
+ i++;
+ if (i >= s.length()) {
+ addToSet(val, end, v2, type);
+ return i;
+ }
+ c = s.charAt(i);
+ if (c >= '0' && c <= '9') {
+ ValueSet vs = getValue(v2, s, i);
+ int v3 = vs.value;
+ addToSet(val, end, v3, type);
+ i = vs.pos;
+ return i;
+ } else {
+ throw new ParseException("Unexpected character '" + c + "' after '/'", i);
+ }
+ }
+
+ addToSet(val, end, 0, type);
+ i++;
+ return i;
+ }
+
+ public String getCronExpression() {
+ return cronExpression;
+ }
+
+ public String getExpressionSummary() {
+ StringBuffer buf = new StringBuffer();
+
+ buf.append("seconds: ");
+ buf.append(getExpressionSetSummary(seconds));
+ buf.append("\n");
+ buf.append("minutes: ");
+ buf.append(getExpressionSetSummary(minutes));
+ buf.append("\n");
+ buf.append("hours: ");
+ buf.append(getExpressionSetSummary(hours));
+ buf.append("\n");
+ buf.append("daysOfMonth: ");
+ buf.append(getExpressionSetSummary(daysOfMonth));
+ buf.append("\n");
+ buf.append("months: ");
+ buf.append(getExpressionSetSummary(months));
+ buf.append("\n");
+ buf.append("daysOfWeek: ");
+ buf.append(getExpressionSetSummary(daysOfWeek));
+ buf.append("\n");
+ buf.append("lastdayOfWeek: ");
+ buf.append(lastdayOfWeek);
+ buf.append("\n");
+ buf.append("nearestWeekday: ");
+ buf.append(nearestWeekday);
+ buf.append("\n");
+ buf.append("NthDayOfWeek: ");
+ buf.append(nthdayOfWeek);
+ buf.append("\n");
+ buf.append("lastdayOfMonth: ");
+ buf.append(lastdayOfMonth);
+ buf.append("\n");
+ buf.append("years: ");
+ buf.append(getExpressionSetSummary(years));
+ buf.append("\n");
+
+ return buf.toString();
+ }
+
+ protected String getExpressionSetSummary(java.util.Set set) {
+
+ if (set.contains(NO_SPEC)) {
+ return "?";
+ }
+ if (set.contains(ALL_SPEC)) {
+ return "*";
+ }
+
+ StringBuffer buf = new StringBuffer();
+
+ Iterator itr = set.iterator();
+ boolean first = true;
+ while (itr.hasNext()) {
+ Integer iVal = (Integer) itr.next();
+ String val = iVal.toString();
+ if (!first) {
+ buf.append(",");
+ }
+ buf.append(val);
+ first = false;
+ }
+
+ return buf.toString();
+ }
+
+ protected String getExpressionSetSummary(java.util.ArrayList list) {
+
+ if (list.contains(NO_SPEC)) {
+ return "?";
+ }
+ if (list.contains(ALL_SPEC)) {
+ return "*";
+ }
+
+ StringBuffer buf = new StringBuffer();
+
+ Iterator itr = list.iterator();
+ boolean first = true;
+ while (itr.hasNext()) {
+ Integer iVal = (Integer) itr.next();
+ String val = iVal.toString();
+ if (!first) {
+ buf.append(",");
+ }
+ buf.append(val);
+ first = false;
+ }
+
+ return buf.toString();
+ }
+
+ protected int skipWhiteSpace(int i, String s) {
+ for (; i < s.length() && (s.charAt(i) == ' ' || s.charAt(i) == '\t'); i++) {
+ ;
+ }
+
+ return i;
+ }
+
+ protected int findNextWhiteSpace(int i, String s) {
+ for (; i < s.length() && (s.charAt(i) != ' ' || s.charAt(i) != '\t'); i++) {
+ ;
+ }
+
+ return i;
+ }
+
+ protected void addToSet(int val, int end, int incr, int type)
+ throws ParseException {
+
+ TreeSet set = getSet(type);
+
+ if (type == SECOND || type == MINUTE) {
+ if ((val < 0 || val > 59 || end > 59) && (val != ALL_SPEC_INT)) {
+ throw new ParseException(
+ "Minute and Second values must be between 0 and 59",
+ -1);
+ }
+ } else if (type == HOUR) {
+ if ((val < 0 || val > 23 || end > 23) && (val != ALL_SPEC_INT)) {
+ throw new ParseException(
+ "Hour values must be between 0 and 23", -1);
+ }
+ } else if (type == DAY_OF_MONTH) {
+ if ((val < 1 || val > 31 || end > 31) && (val != ALL_SPEC_INT)
+ && (val != NO_SPEC_INT)) {
+ throw new ParseException(
+ "Day of month values must be between 1 and 31", -1);
+ }
+ } else if (type == MONTH) {
+ if ((val < 1 || val > 12 || end > 12) && (val != ALL_SPEC_INT)) {
+ throw new ParseException(
+ "Month values must be between 1 and 12", -1);
+ }
+ } else if (type == DAY_OF_WEEK) {
+ if ((val == 0 || val > 7 || end > 7) && (val != ALL_SPEC_INT)
+ && (val != NO_SPEC_INT)) {
+ throw new ParseException(
+ "Day-of-Week values must be between 1 and 7", -1);
+ }
+ }
+
+ if ((incr == 0 || incr == -1) && val != ALL_SPEC_INT) {
+ if (val != -1) {
+ set.add(new Integer(val));
+ } else {
+ set.add(NO_SPEC);
+ }
+
+ return;
+ }
+
+ int startAt = val;
+ int stopAt = end;
+
+ if (val == ALL_SPEC_INT && incr <= 0) {
+ incr = 1;
+ set.add(ALL_SPEC); // put in a marker, but also fill values
+ }
+
+ if (type == SECOND || type == MINUTE) {
+ if (stopAt == -1) {
+ stopAt = 59;
+ }
+ if (startAt == -1 || startAt == ALL_SPEC_INT) {
+ startAt = 0;
+ }
+ } else if (type == HOUR) {
+ if (stopAt == -1) {
+ stopAt = 23;
+ }
+ if (startAt == -1 || startAt == ALL_SPEC_INT) {
+ startAt = 0;
+ }
+ } else if (type == DAY_OF_MONTH) {
+ if (stopAt == -1) {
+ stopAt = 31;
+ }
+ if (startAt == -1 || startAt == ALL_SPEC_INT) {
+ startAt = 1;
+ }
+ } else if (type == MONTH) {
+ if (stopAt == -1) {
+ stopAt = 12;
+ }
+ if (startAt == -1 || startAt == ALL_SPEC_INT) {
+ startAt = 1;
+ }
+ } else if (type == DAY_OF_WEEK) {
+ if (stopAt == -1) {
+ stopAt = 7;
+ }
+ if (startAt == -1 || startAt == ALL_SPEC_INT) {
+ startAt = 1;
+ }
+ } else if (type == YEAR) {
+ if (stopAt == -1) {
+ stopAt = 2099;
+ }
+ if (startAt == -1 || startAt == ALL_SPEC_INT) {
+ startAt = 1970;
+ }
+ }
+
+ // if the end of the range is before the start, then we need to overflow into
+ // the next day, month etc. This is done by adding the maximum amount for that
+ // type, and using modulus max to determine the value being added.
+ int max = -1;
+ if (stopAt < startAt) {
+ switch (type) {
+ case SECOND : max = 60; break;
+ case MINUTE : max = 60; break;
+ case HOUR : max = 24; break;
+ case MONTH : max = 12; break;
+ case DAY_OF_WEEK : max = 7; break;
+ case DAY_OF_MONTH : max = 31; break;
+ case YEAR : throw new IllegalArgumentException("Start year must be less than stop year");
+ default : throw new IllegalArgumentException("Unexpected type encountered");
+ }
+ stopAt += max;
+ }
+
+ for (int i = startAt; i <= stopAt; i += incr) {
+ if (max == -1) {
+ // ie: there's no max to overflow over
+ set.add(new Integer(i));
+ } else {
+ // take the modulus to get the real value
+ int i2 = i % max;
+
+ // 1-indexed ranges should not include 0, and should include their max
+ if (i2 == 0 && (type == MONTH || type == DAY_OF_WEEK || type == DAY_OF_MONTH) ) {
+ i2 = max;
+ }
+
+ set.add(new Integer(i2));
+ }
+ }
+ }
+
+ protected TreeSet getSet(int type) {
+ switch (type) {
+ case SECOND:
+ return seconds;
+ case MINUTE:
+ return minutes;
+ case HOUR:
+ return hours;
+ case DAY_OF_MONTH:
+ return daysOfMonth;
+ case MONTH:
+ return months;
+ case DAY_OF_WEEK:
+ return daysOfWeek;
+ case YEAR:
+ return years;
+ default:
+ return null;
+ }
+ }
+
+ protected ValueSet getValue(int v, String s, int i) {
+ char c = s.charAt(i);
+ String s1 = String.valueOf(v);
+ while (c >= '0' && c <= '9') {
+ s1 += c;
+ i++;
+ if (i >= s.length()) {
+ break;
+ }
+ c = s.charAt(i);
+ }
+ ValueSet val = new ValueSet();
+
+ val.pos = (i < s.length()) ? i : i + 1;
+ val.value = Integer.parseInt(s1);
+ return val;
+ }
+
+ protected int getNumericValue(String s, int i) {
+ int endOfVal = findNextWhiteSpace(i, s);
+ String val = s.substring(i, endOfVal);
+ return Integer.parseInt(val);
+ }
+
+ protected int getMonthNumber(String s) {
+ Integer integer = (Integer) monthMap.get(s);
+
+ if (integer == null) {
+ return -1;
+ }
+
+ return integer.intValue();
+ }
+
+ protected int getDayOfWeekNumber(String s) {
+ Integer integer = (Integer) dayMap.get(s);
+
+ if (integer == null) {
+ return -1;
+ }
+
+ return integer.intValue();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Computation Functions
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected Date getTimeAfter(Date afterTime) {
+
+ Calendar cl = Calendar.getInstance(getTimeZone());
+
+ // move ahead one second, since we're computing the time *after* the
+ // given time
+ afterTime = new Date(afterTime.getTime() + 1000);
+ // CronTrigger does not deal with milliseconds
+ cl.setTime(afterTime);
+ cl.set(Calendar.MILLISECOND, 0);
+
+ boolean gotOne = false;
+ // loop until we've computed the next time, or we've past the endTime
+ while (!gotOne) {
+
+ //if (endTime != null && cl.getTime().after(endTime)) return null;
+ if(cl.get(Calendar.YEAR) > 2999) { // prevent endless loop...
+ return null;
+ }
+
+ SortedSet st = null;
+ int t = 0;
+
+ int sec = cl.get(Calendar.SECOND);
+ int min = cl.get(Calendar.MINUTE);
+
+ // get second.................................................
+ st = seconds.tailSet(new Integer(sec));
+ if (st != null && st.size() != 0) {
+ sec = ((Integer) st.first()).intValue();
+ } else {
+ sec = ((Integer) seconds.first()).intValue();
+ min++;
+ cl.set(Calendar.MINUTE, min);
+ }
+ cl.set(Calendar.SECOND, sec);
+
+ min = cl.get(Calendar.MINUTE);
+ int hr = cl.get(Calendar.HOUR_OF_DAY);
+ t = -1;
+
+ // get minute.................................................
+ st = minutes.tailSet(new Integer(min));
+ if (st != null && st.size() != 0) {
+ t = min;
+ min = ((Integer) st.first()).intValue();
+ } else {
+ min = ((Integer) minutes.first()).intValue();
+ hr++;
+ }
+ if (min != t) {
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, min);
+ setCalendarHour(cl, hr);
+ continue;
+ }
+ cl.set(Calendar.MINUTE, min);
+
+ hr = cl.get(Calendar.HOUR_OF_DAY);
+ int day = cl.get(Calendar.DAY_OF_MONTH);
+ t = -1;
+
+ // get hour...................................................
+ st = hours.tailSet(new Integer(hr));
+ if (st != null && st.size() != 0) {
+ t = hr;
+ hr = ((Integer) st.first()).intValue();
+ } else {
+ hr = ((Integer) hours.first()).intValue();
+ day++;
+ }
+ if (hr != t) {
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.DAY_OF_MONTH, day);
+ setCalendarHour(cl, hr);
+ continue;
+ }
+ cl.set(Calendar.HOUR_OF_DAY, hr);
+
+ day = cl.get(Calendar.DAY_OF_MONTH);
+ int mon = cl.get(Calendar.MONTH) + 1;
+ // '+ 1' because calendar is 0-based for this field, and we are
+ // 1-based
+ t = -1;
+ int tmon = mon;
+
+ // get day...................................................
+ boolean dayOfMSpec = !daysOfMonth.contains(NO_SPEC);
+ boolean dayOfWSpec = !daysOfWeek.contains(NO_SPEC);
+ if (dayOfMSpec && !dayOfWSpec) { // get day by day of month rule
+ st = daysOfMonth.tailSet(new Integer(day));
+ if (lastdayOfMonth) {
+ if(!nearestWeekday) {
+ t = day;
+ day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
+ } else {
+ t = day;
+ day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
+
+ java.util.Calendar tcal = java.util.Calendar.getInstance(getTimeZone());
+ tcal.set(Calendar.SECOND, 0);
+ tcal.set(Calendar.MINUTE, 0);
+ tcal.set(Calendar.HOUR_OF_DAY, 0);
+ tcal.set(Calendar.DAY_OF_MONTH, day);
+ tcal.set(Calendar.MONTH, mon - 1);
+ tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR));
+
+ int ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
+ int dow = tcal.get(Calendar.DAY_OF_WEEK);
+
+ if(dow == Calendar.SATURDAY && day == 1) {
+ day += 2;
+ } else if(dow == Calendar.SATURDAY) {
+ day -= 1;
+ } else if(dow == Calendar.SUNDAY && day == ldom) {
+ day -= 2;
+ } else if(dow == Calendar.SUNDAY) {
+ day += 1;
+ }
+
+ tcal.set(Calendar.SECOND, sec);
+ tcal.set(Calendar.MINUTE, min);
+ tcal.set(Calendar.HOUR_OF_DAY, hr);
+ tcal.set(Calendar.DAY_OF_MONTH, day);
+ tcal.set(Calendar.MONTH, mon - 1);
+ Date nTime = tcal.getTime();
+ if(nTime.before(afterTime)) {
+ day = 1;
+ mon++;
+ }
+ }
+ } else if(nearestWeekday) {
+ t = day;
+ day = ((Integer) daysOfMonth.first()).intValue();
+
+ java.util.Calendar tcal = java.util.Calendar.getInstance(getTimeZone());
+ tcal.set(Calendar.SECOND, 0);
+ tcal.set(Calendar.MINUTE, 0);
+ tcal.set(Calendar.HOUR_OF_DAY, 0);
+ tcal.set(Calendar.DAY_OF_MONTH, day);
+ tcal.set(Calendar.MONTH, mon - 1);
+ tcal.set(Calendar.YEAR, cl.get(Calendar.YEAR));
+
+ int ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
+ int dow = tcal.get(Calendar.DAY_OF_WEEK);
+
+ if(dow == Calendar.SATURDAY && day == 1) {
+ day += 2;
+ } else if(dow == Calendar.SATURDAY) {
+ day -= 1;
+ } else if(dow == Calendar.SUNDAY && day == ldom) {
+ day -= 2;
+ } else if(dow == Calendar.SUNDAY) {
+ day += 1;
+ }
+
+
+ tcal.set(Calendar.SECOND, sec);
+ tcal.set(Calendar.MINUTE, min);
+ tcal.set(Calendar.HOUR_OF_DAY, hr);
+ tcal.set(Calendar.DAY_OF_MONTH, day);
+ tcal.set(Calendar.MONTH, mon - 1);
+ Date nTime = tcal.getTime();
+ if(nTime.before(afterTime)) {
+ day = ((Integer) daysOfMonth.first()).intValue();
+ mon++;
+ }
+ } else if (st != null && st.size() != 0) {
+ t = day;
+ day = ((Integer) st.first()).intValue();
+ // make sure we don't over-run a short month, such as february
+ int lastDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
+ if (day > lastDay) {
+ day = ((Integer) daysOfMonth.first()).intValue();
+ mon++;
+ }
+ } else {
+ day = ((Integer) daysOfMonth.first()).intValue();
+ mon++;
+ }
+
+ if (day != t || mon != tmon) {
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.HOUR_OF_DAY, 0);
+ cl.set(Calendar.DAY_OF_MONTH, day);
+ cl.set(Calendar.MONTH, mon - 1);
+ // '- 1' because calendar is 0-based for this field, and we
+ // are 1-based
+ continue;
+ }
+ } else if (dayOfWSpec && !dayOfMSpec) { // get day by day of week rule
+ if (lastdayOfWeek) { // are we looking for the last XXX day of
+ // the month?
+ int dow = ((Integer) daysOfWeek.first()).intValue(); // desired
+ // d-o-w
+ int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w
+ int daysToAdd = 0;
+ if (cDow < dow) {
+ daysToAdd = dow - cDow;
+ }
+ if (cDow > dow) {
+ daysToAdd = dow + (7 - cDow);
+ }
+
+ int lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
+
+ if (day + daysToAdd > lDay) { // did we already miss the
+ // last one?
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.HOUR_OF_DAY, 0);
+ cl.set(Calendar.DAY_OF_MONTH, 1);
+ cl.set(Calendar.MONTH, mon);
+ // no '- 1' here because we are promoting the month
+ continue;
+ }
+
+ // find date of last occurance of this day in this month...
+ while ((day + daysToAdd + 7) <= lDay) {
+ daysToAdd += 7;
+ }
+
+ day += daysToAdd;
+
+ if (daysToAdd > 0) {
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.HOUR_OF_DAY, 0);
+ cl.set(Calendar.DAY_OF_MONTH, day);
+ cl.set(Calendar.MONTH, mon - 1);
+ // '- 1' here because we are not promoting the month
+ continue;
+ }
+
+ } else if (nthdayOfWeek != 0) {
+ // are we looking for the Nth XXX day in the month?
+ int dow = ((Integer) daysOfWeek.first()).intValue(); // desired
+ // d-o-w
+ int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w
+ int daysToAdd = 0;
+ if (cDow < dow) {
+ daysToAdd = dow - cDow;
+ } else if (cDow > dow) {
+ daysToAdd = dow + (7 - cDow);
+ }
+
+ boolean dayShifted = false;
+ if (daysToAdd > 0) {
+ dayShifted = true;
+ }
+
+ day += daysToAdd;
+ int weekOfMonth = day / 7;
+ if (day % 7 > 0) {
+ weekOfMonth++;
+ }
+
+ daysToAdd = (nthdayOfWeek - weekOfMonth) * 7;
+ day += daysToAdd;
+ if (daysToAdd < 0
+ || day > getLastDayOfMonth(mon, cl
+ .get(Calendar.YEAR))) {
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.HOUR_OF_DAY, 0);
+ cl.set(Calendar.DAY_OF_MONTH, 1);
+ cl.set(Calendar.MONTH, mon);
+ // no '- 1' here because we are promoting the month
+ continue;
+ } else if (daysToAdd > 0 || dayShifted) {
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.HOUR_OF_DAY, 0);
+ cl.set(Calendar.DAY_OF_MONTH, day);
+ cl.set(Calendar.MONTH, mon - 1);
+ // '- 1' here because we are NOT promoting the month
+ continue;
+ }
+ } else {
+ int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w
+ int dow = ((Integer) daysOfWeek.first()).intValue(); // desired
+ // d-o-w
+ st = daysOfWeek.tailSet(new Integer(cDow));
+ if (st != null && st.size() > 0) {
+ dow = ((Integer) st.first()).intValue();
+ }
+
+ int daysToAdd = 0;
+ if (cDow < dow) {
+ daysToAdd = dow - cDow;
+ }
+ if (cDow > dow) {
+ daysToAdd = dow + (7 - cDow);
+ }
+
+ int lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR));
+
+ if (day + daysToAdd > lDay) { // will we pass the end of
+ // the month?
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.HOUR_OF_DAY, 0);
+ cl.set(Calendar.DAY_OF_MONTH, 1);
+ cl.set(Calendar.MONTH, mon);
+ // no '- 1' here because we are promoting the month
+ continue;
+ } else if (daysToAdd > 0) { // are we swithing days?
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.HOUR_OF_DAY, 0);
+ cl.set(Calendar.DAY_OF_MONTH, day + daysToAdd);
+ cl.set(Calendar.MONTH, mon - 1);
+ // '- 1' because calendar is 0-based for this field,
+ // and we are 1-based
+ continue;
+ }
+ }
+ } else { // dayOfWSpec && !dayOfMSpec
+ throw new UnsupportedOperationException(
+ "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented.");
+ // TODO:
+ }
+ cl.set(Calendar.DAY_OF_MONTH, day);
+
+ mon = cl.get(Calendar.MONTH) + 1;
+ // '+ 1' because calendar is 0-based for this field, and we are
+ // 1-based
+ int year = cl.get(Calendar.YEAR);
+ t = -1;
+
+ // test for expressions that never generate a valid fire date,
+ // but keep looping...
+ if (year > 2099) {
+ return null;
+ }
+
+ // get month...................................................
+ st = months.tailSet(new Integer(mon));
+ if (st != null && st.size() != 0) {
+ t = mon;
+ mon = ((Integer) st.first()).intValue();
+ } else {
+ mon = ((Integer) months.first()).intValue();
+ year++;
+ }
+ if (mon != t) {
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.HOUR_OF_DAY, 0);
+ cl.set(Calendar.DAY_OF_MONTH, 1);
+ cl.set(Calendar.MONTH, mon - 1);
+ // '- 1' because calendar is 0-based for this field, and we are
+ // 1-based
+ cl.set(Calendar.YEAR, year);
+ continue;
+ }
+ cl.set(Calendar.MONTH, mon - 1);
+ // '- 1' because calendar is 0-based for this field, and we are
+ // 1-based
+
+ year = cl.get(Calendar.YEAR);
+ t = -1;
+
+ // get year...................................................
+ st = years.tailSet(new Integer(year));
+ if (st != null && st.size() != 0) {
+ t = year;
+ year = ((Integer) st.first()).intValue();
+ } else {
+ return null; // ran out of years...
+ }
+
+ if (year != t) {
+ cl.set(Calendar.SECOND, 0);
+ cl.set(Calendar.MINUTE, 0);
+ cl.set(Calendar.HOUR_OF_DAY, 0);
+ cl.set(Calendar.DAY_OF_MONTH, 1);
+ cl.set(Calendar.MONTH, 0);
+ // '- 1' because calendar is 0-based for this field, and we are
+ // 1-based
+ cl.set(Calendar.YEAR, year);
+ continue;
+ }
+ cl.set(Calendar.YEAR, year);
+
+ gotOne = true;
+ } // while( !done )
+
+ return cl.getTime();
+ }
+
+ /**
+ * Advance the calendar to the particular hour paying particular attention
+ * to daylight saving problems.
+ *
+ * @param cal
+ * @param hour
+ */
+ protected void setCalendarHour(Calendar cal, int hour) {
+ cal.set(java.util.Calendar.HOUR_OF_DAY, hour);
+ if (cal.get(java.util.Calendar.HOUR_OF_DAY) != hour && hour != 24) {
+ cal.set(java.util.Calendar.HOUR_OF_DAY, hour + 1);
+ }
+ }
+
+ /**
+ * NOT YET IMPLEMENTED: Returns the time before the given time
+ * that the CronExpression
matches.
+ */
+ protected Date getTimeBefore(Date endTime) {
+ // TODO: implement QUARTZ-423
+ return null;
+ }
+
+ /**
+ * NOT YET IMPLEMENTED: Returns the final time that the
+ * CronExpression
will match.
+ */
+ public Date getFinalFireTime() {
+ // TODO: implement QUARTZ-423
+ return null;
+ }
+
+ protected boolean isLeapYear(int year) {
+ return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
+ }
+
+ protected int getLastDayOfMonth(int monthNum, int year) {
+
+ switch (monthNum) {
+ case 1:
+ return 31;
+ case 2:
+ return (isLeapYear(year)) ? 29 : 28;
+ case 3:
+ return 31;
+ case 4:
+ return 30;
+ case 5:
+ return 31;
+ case 6:
+ return 30;
+ case 7:
+ return 31;
+ case 8:
+ return 31;
+ case 9:
+ return 30;
+ case 10:
+ return 31;
+ case 11:
+ return 30;
+ case 12:
+ return 31;
+ default:
+ throw new IllegalArgumentException("Illegal month number: "
+ + monthNum);
+ }
+ }
+
+
+ private void readObject(java.io.ObjectInputStream stream)
+ throws java.io.IOException, ClassNotFoundException {
+
+ stream.defaultReadObject();
+ try {
+ buildExpression(cronExpression);
+ } catch (Exception ignore) {
+ } // never happens
+ }
+
+ public Object clone() {
+ CronExpression copy = null;
+ try {
+ copy = new CronExpression(getCronExpression());
+ copy.setTimeZone(getTimeZone());
+ } catch (ParseException ex) { // never happens since the source is valid...
+ throw new IncompatibleClassChangeError("Not Cloneable.");
+ }
+ return copy;
+ }
+}
+
+class ValueSet {
+ public int value;
+
+ public int pos;
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/CronTrigger.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/CronTrigger.java
new file mode 100644
index 000000000..9580cec92
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/CronTrigger.java
@@ -0,0 +1,1075 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz;
+
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+/**
+ *
+ * A concrete {@link Trigger}
that is used to fire a {@link com.fr.third.org.quartz.JobDetail}
+ * at given moments in time, defined with Unix 'cron-like' definitions.
+ *
+ * For those unfamiliar with "cron", this means being able to create a firing + * schedule such as: "At 8:00am every Monday through Friday" or "At 1:30am + * every last Friday of the month". + *
+ * + *+ * The format of a "Cron-Expression" string is documented on the + * {@link com.fr.third.org.quartz.CronExpression} class. + *
+ * + *
+ * Here are some full examples:
Expression | + *+ * | Meaning | + *
---|---|---|
"0 0 12 * * ?" |
+ * + * | Fire at 12pm (noon) every day |
+ *
"0 15 10 ? * *" |
+ * + * | Fire at 10:15am every day |
+ *
"0 15 10 * * ?" |
+ * + * | Fire at 10:15am every day |
+ *
"0 15 10 * * ? *" |
+ * + * | Fire at 10:15am every day |
+ *
"0 15 10 * * ? 2005" |
+ * + * | Fire at 10:15am every day during the year 2005
+ * |
+ *
"0 * 14 * * ?" |
+ * + * | Fire every minute starting at 2pm and ending at 2:59pm, every day
+ * |
+ *
"0 0/5 14 * * ?" |
+ * + * | Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day
+ * |
+ *
"0 0/5 14,18 * * ?" |
+ * + * | Fire every 5 minutes starting at 2pm and ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending at 6:55pm, every day
+ * |
+ *
"0 0-5 14 * * ?" |
+ * + * | Fire every minute starting at 2pm and ending at 2:05pm, every day
+ * |
+ *
"0 10,44 14 ? 3 WED" |
+ * + * | Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.
+ * |
+ *
"0 15 10 ? * MON-FRI" |
+ * + * | Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday
+ * |
+ *
"0 15 10 15 * ?" |
+ * + * | Fire at 10:15am on the 15th day of every month
+ * |
+ *
"0 15 10 L * ?" |
+ * + * | Fire at 10:15am on the last day of every month
+ * |
+ *
"0 15 10 ? * 6L" |
+ * + * | Fire at 10:15am on the last Friday of every month
+ * |
+ *
"0 15 10 ? * 6L" |
+ * + * | Fire at 10:15am on the last Friday of every month
+ * |
+ *
"0 15 10 ? * 6L 2002-2005" |
+ * + * | Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005
+ * |
+ *
"0 15 10 ? * 6#3" |
+ * + * | Fire at 10:15am on the third Friday of every month
+ * |
+ *
+ * Pay attention to the effects of '?' and '*' in the day-of-week and + * day-of-month fields! + *
+ * + *+ * NOTES: + *
+ * Instructs the {@link Scheduler}
that upon a mis-fire
+ * situation, the {@link CronTrigger}
wants to be fired now
+ * by Scheduler
.
+ *
+ * Instructs the {@link Scheduler}
that upon a mis-fire
+ * situation, the {@link CronTrigger}
wants to have it's
+ * next-fire-time updated to the next time in the schedule after the
+ * current time (taking into account any associated {@link Calendar}
,
+ * but it does not want to be fired now.
+ *
+ * Create a CronTrigger
with no settings.
+ *
+ * The start-time will also be set to the current time, and the time zone + * will be set the the system's default time zone. + *
+ */ + public CronTrigger() { + super(); + setStartTime(new Date()); + setTimeZone(TimeZone.getDefault()); + } + + /** + *
+ * Create a CronTrigger
with the given name and group.
+ *
+ * The start-time will also be set to the current time, and the time zone + * will be set the the system's default time zone. + *
+ */ + public CronTrigger(String name, String group) { + super(name, group); + setStartTime(new Date()); + setTimeZone(TimeZone.getDefault()); + } + + /** + *
+ * Create a CronTrigger
with the given name, group and
+ * expression.
+ *
+ * The start-time will also be set to the current time, and the time zone + * will be set the the system's default time zone. + *
+ */ + public CronTrigger(String name, String group, String cronExpression) + throws ParseException { + + super(name, group); + + setCronExpression(cronExpression); + + setStartTime(new Date()); + setTimeZone(TimeZone.getDefault()); + } + + /** + *
+ * Create a CronTrigger
with the given name and group, and
+ * associated with the identified {@link com.fr.third.org.quartz.JobDetail}
.
+ *
+ * The start-time will also be set to the current time, and the time zone + * will be set the the system's default time zone. + *
+ */ + public CronTrigger(String name, String group, String jobName, + String jobGroup) { + super(name, group, jobName, jobGroup); + setStartTime(new Date()); + setTimeZone(TimeZone.getDefault()); + } + + /** + *
+ * Create a CronTrigger
with the given name and group,
+ * associated with the identified {@link com.fr.third.org.quartz.JobDetail}
,
+ * and with the given "cron" expression.
+ *
+ * The start-time will also be set to the current time, and the time zone + * will be set the the system's default time zone. + *
+ */ + public CronTrigger(String name, String group, String jobName, + String jobGroup, String cronExpression) throws ParseException { + this(name, group, jobName, jobGroup, null, null, cronExpression, + TimeZone.getDefault()); + } + + /** + *
+ * Create a CronTrigger
with the given name and group,
+ * associated with the identified {@link com.fr.third.org.quartz.JobDetail}
,
+ * and with the given "cron" expression resolved with respect to the TimeZone
.
+ *
+ * Create a CronTrigger
that will occur at the given time,
+ * until the given end time.
+ *
+ * If null, the start-time will also be set to the current time, the time + * zone will be set the the system's default. + *
+ * + * @param startTime + * ADate
set to the time for the Trigger
+ * to fire.
+ * @param endTime
+ * A Date
set to the time for the Trigger
+ * to quit repeat firing.
+ */
+ public CronTrigger(String name, String group, String jobName,
+ String jobGroup, Date startTime, Date endTime, String cronExpression)
+ throws ParseException {
+ super(name, group, jobName, jobGroup);
+
+ setCronExpression(cronExpression);
+
+ if (startTime == null) {
+ startTime = new Date();
+ }
+ setStartTime(startTime);
+ if (endTime != null) {
+ setEndTime(endTime);
+ }
+ setTimeZone(TimeZone.getDefault());
+
+ }
+
+ /**
+ *
+ * Create a CronTrigger
with fire time dictated by the
+ * cronExpression
resolved with respect to the specified
+ * timeZone
occuring from the startTime
until
+ * the given endTime
.
+ *
+ * If null, the start-time will also be set to the current time. If null, + * the time zone will be set to the system's default. + *
+ * + * @param name + * of theTrigger
+ * @param group
+ * of the Trigger
+ * @param jobName
+ * name of the {@link com.fr.third.org.quartz.JobDetail}
+ * executed on firetime
+ * @param jobGroup
+ * group of the {@link com.fr.third.org.quartz.JobDetail}
+ * executed on firetime
+ * @param startTime
+ * A Date
set to the earliest time for the Trigger
+ * to start firing.
+ * @param endTime
+ * A Date
set to the time for the Trigger
+ * to quit repeat firing.
+ * @param cronExpression
+ * A cron expression dictating the firing sequence of the Trigger
+ * @param timeZone
+ * Specifies for which time zone the cronExpression
+ * should be interprted, i.e. the expression 0 0 10 * * ?, is
+ * resolved to 10:00 am in this time zone.
+ * @throws ParseException
+ * if the cronExpression
is invalid.
+ */
+ public CronTrigger(String name, String group, String jobName,
+ String jobGroup, Date startTime, Date endTime,
+ String cronExpression, TimeZone timeZone) throws ParseException {
+ super(name, group, jobName, jobGroup);
+
+ setCronExpression(cronExpression);
+
+ if (startTime == null) {
+ startTime = new Date();
+ }
+ setStartTime(startTime);
+ if (endTime != null) {
+ setEndTime(endTime);
+ }
+ if (timeZone == null) {
+ setTimeZone(TimeZone.getDefault());
+ } else {
+ setTimeZone(timeZone);
+ }
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public Object clone() {
+ CronTrigger copy = (CronTrigger) super.clone();
+ if (cronEx != null) {
+ copy.setCronExpression((CronExpression)cronEx.clone());
+ }
+ return copy;
+ }
+
+ public void setCronExpression(String cronExpression) throws ParseException {
+ TimeZone origTz = getTimeZone();
+ this.cronEx = new CronExpression(cronExpression);
+ this.cronEx.setTimeZone(origTz);
+ }
+
+ public String getCronExpression() {
+ return cronEx == null ? null : cronEx.getCronExpression();
+ }
+
+ /**
+ * Set the CronExpression to the given one. The TimeZone on the passed-in
+ * CronExpression over-rides any that was already set on the Trigger.
+ *
+ * @param cronExpression
+ */
+ public void setCronExpression(CronExpression cronExpression) {
+ this.cronEx = cronExpression;
+ this.timeZone = cronExpression.getTimeZone();
+ }
+
+ /**
+ *
+ * Get the time at which the CronTrigger
should occur.
+ *
+ * Get the time at which the CronTrigger
should quit
+ * repeating - even if repeastCount isn't yet satisfied.
+ *
+ * Returns the next time at which the Trigger
is scheduled to fire. If
+ * the trigger will not fire again, null
will be returned. Note that
+ * the time returned can possibly be in the past, if the time that was computed
+ * for the trigger to next fire has already arrived, but the scheduler has not yet
+ * been able to fire the trigger (which would likely be due to lack of resources
+ * e.g. threads).
+ *
The value returned is not guaranteed to be valid until after the Trigger
+ * has been added to the scheduler.
+ *
+ * Returns the previous time at which the CronTrigger
+ * fired. If the trigger has not yet fired, null
will be
+ * returned.
+ */
+ public Date getPreviousFireTime() {
+ return this.previousFireTime;
+ }
+
+ /**
+ *
+ * Sets the next time at which the CronTrigger
will fire.
+ * This method should not be invoked by client code.
+ *
+ * Set the previous time at which the CronTrigger
fired.
+ *
+ * This method should not be invoked by client code. + *
+ */ + public void setPreviousFireTime(Date previousFireTime) { + this.previousFireTime = previousFireTime; + } + + /** + *
+ * Returns the time zone for which the cronExpression
of
+ * this CronTrigger
will be resolved.
+ *
+ * Sets the time zone for which the cronExpression
of this
+ * CronTrigger
will be resolved.
+ *
If {@link #setCronExpression(CronExpression)} is called after this + * method, the TimeZon setting on the CronExpression will "win". However + * if {@link #setCronExpression(String)} is called after this method, the + * time zone applied by this method will remain in effect, since the + * String cron expression does not carry a time zone! + */ + public void setTimeZone(TimeZone timeZone) { + if(cronEx != null) { + cronEx.setTimeZone(timeZone); + } + this.timeZone = timeZone; + } + + /** + *
+ * Returns the next time at which the CronTrigger
will fire,
+ * after the given time. If the trigger will not fire after the given time,
+ * null
will be returned.
+ *
+ * Note that the date returned is NOT validated against the related + * com.fr.third.org.quartz.Calendar (if any) + *
+ */ + public Date getFireTimeAfter(Date afterTime) { + if (afterTime == null) { + afterTime = new Date(); + } + + if (getStartTime().after(afterTime)) { + afterTime = new Date(getStartTime().getTime() - 1000l); + } + + if (getEndTime() != null && (afterTime.compareTo(getEndTime()) >= 0)) { + return null; + } + + Date pot = getTimeAfter(afterTime); + if (getEndTime() != null && pot != null && pot.after(getEndTime())) { + return null; + } + + return pot; + } + + /** + *
+ * NOT YET IMPLEMENTED: Returns the final time at which the
+ * CronTrigger
will fire.
+ *
+ * Note that the return time *may* be in the past. and the date returned is + * not validated against com.fr.third.org.quartz.calendar + *
+ */ + public Date getFinalFireTime() { + Date resultTime; + if (getEndTime() != null) { + resultTime = getTimeBefore(new Date(getEndTime().getTime() + 1000l)); + } else { + resultTime = (cronEx == null) ? null : cronEx.getFinalFireTime(); + } + + if ((resultTime != null) && (getStartTime() != null) && (resultTime.before(getStartTime()))) { + return null; + } + + return resultTime; + } + + /** + *
+ * Determines whether or not the CronTrigger
will occur
+ * again.
+ *
+ * Updates the CronTrigger
's state based on the
+ * MISFIRE_INSTRUCTION_XXX that was selected when the CronTrigger
+ * was created.
+ *
+ * If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY,
+ * then the following scheme will be used:
+ *
MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
+ * + * Determines whether the date and (optionally) time of the given Calendar + * instance falls on a scheduled fire-time of this trigger. + *
+ * + *
+ * Equivalent to calling willFireOn(cal, false)
.
+ *
+ * Determines whether the date and (optionally) time of the given Calendar + * instance falls on a scheduled fire-time of this trigger. + *
+ * + *+ * Note that the value returned is NOT validated against the related + * com.fr.third.org.quartz.Calendar (if any) + *
+ * + * @param test the date to compare + * @param dayOnly if set to true, the method will only determine if the + * trigger will fire during the day represented by the given Calendar + * (hours, minutes and seconds will be ignored). + * @see #willFireOn(Calendar) + */ + public boolean willFireOn(Calendar test, boolean dayOnly) { + + test = (Calendar) test.clone(); + + test.set(Calendar.MILLISECOND, 0); // don't compare millis. + + if(dayOnly) { + test.set(Calendar.HOUR, 0); + test.set(Calendar.MINUTE, 0); + test.set(Calendar.SECOND, 0); + } + + Date testTime = test.getTime(); + + Date fta = getFireTimeAfter(new Date(test.getTime().getTime() - 1000)); + + Calendar p = Calendar.getInstance(test.getTimeZone()); + p.setTime(fta); + + int year = p.get(Calendar.YEAR); + int month = p.get(Calendar.MONTH); + int day = p.get(Calendar.DATE); + + if(dayOnly) { + return (year == test.get(Calendar.YEAR) + && month == test.get(Calendar.MONTH) + && day == test.get(Calendar.DATE)); + } + + while(fta.before(testTime)) { + fta = getFireTimeAfter(fta); + } + + if(fta.equals(testTime)) { + return true; + } + + return false; + } + + /** + *
+ * Called after the {@link Scheduler}
has executed the
+ * {@link com.fr.third.org.quartz.JobDetail}
associated with the Trigger
+ * in order to get the final instruction code from the trigger.
+ *
JobExecutionContext
that was used by the
+ * Job
'sexecute(xx)
method.
+ * @param result
+ * is the JobExecutionException
thrown by the
+ * Job
, if any (may be null).
+ * @return one of the Trigger.INSTRUCTION_XXX constants.
+ *
+ * @see #INSTRUCTION_NOOP
+ * @see #INSTRUCTION_RE_EXECUTE_JOB
+ * @see #INSTRUCTION_DELETE_TRIGGER
+ * @see #INSTRUCTION_SET_TRIGGER_COMPLETE
+ * @see #triggered(Calendar)
+ */
+ public int executionComplete(JobExecutionContext context,
+ JobExecutionException result) {
+ if (result != null && result.refireImmediately()) {
+ return INSTRUCTION_RE_EXECUTE_JOB;
+ }
+
+ if (result != null && result.unscheduleFiringTrigger()) {
+ return INSTRUCTION_SET_TRIGGER_COMPLETE;
+ }
+
+ if (result != null && result.unscheduleAllTriggers()) {
+ return INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE;
+ }
+
+ if (!mayFireAgain()) {
+ return INSTRUCTION_DELETE_TRIGGER;
+ }
+
+ return INSTRUCTION_NOOP;
+ }
+
+ /**
+ *
+ * Called when the {@link Scheduler}
has decided to 'fire'
+ * the trigger (execute the associated Job
), in order to
+ * give the Trigger
a chance to update itself for its next
+ * triggering (if any).
+ *
+ * Called by the scheduler at the time a Trigger
is first
+ * added to the scheduler, in order to have the Trigger
+ * compute its first fire time, based on any associated calendar.
+ *
+ * After this method has been called, getNextFireTime()
+ * should return a valid answer.
+ *
Trigger
will be fired
+ * by the scheduler, which is also the same value getNextFireTime()
+ * will return (until after the first firing of the Trigger
).
+ *
+ */
+ public Date computeFirstFireTime(com.fr.third.org.quartz.Calendar calendar) {
+ nextFireTime = getFireTimeAfter(new Date(getStartTime().getTime() - 1000l));
+
+ while (nextFireTime != null && calendar != null
+ && !calendar.isTimeIncluded(nextFireTime.getTime())) {
+ nextFireTime = getFireTimeAfter(nextFireTime);
+ }
+
+ return nextFireTime;
+ }
+
+ public String getExpressionSummary() {
+ return cronEx == null ? null : cronEx.getExpressionSummary();
+ }
+
+ /**
+ * Used by extensions of CronTrigger to imply that there are additional
+ * properties, specifically so that extensions can choose whether to be
+ * stored as a serialized blob, or as a flattened CronTrigger table.
+ */
+ public boolean hasAdditionalProperties() {
+ return false;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Computation Functions
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ protected Date getTimeAfter(Date afterTime) {
+ return (cronEx == null) ? null : cronEx.getTimeAfter(afterTime);
+ }
+
+ /**
+ * NOT YET IMPLEMENTED: Returns the time before the given time
+ * that this CronTrigger
will fire.
+ */
+ protected Date getTimeBefore(Date endTime) {
+ return (cronEx == null) ? null : cronEx.getTimeBefore(endTime);
+ }
+
+ public static void main(String[] args) // TODO: remove method after good
+ // unit testing
+ throws Exception {
+
+ String expr = "15 10 0/4 * * ?";
+ if(args != null && args.length > 0 && args[0] != null) {
+ expr = args[0];
+ }
+
+ CronTrigger ct = new CronTrigger("t", "g", "j", "g", new Date(), null, expr);
+ ct.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
+ System.err.println(ct.getExpressionSummary());
+ System.err.println("tz=" + ct.getTimeZone().getID());
+ System.err.println();
+
+ java.util.List times = TriggerUtils.computeFireTimes(ct, null, 25);
+
+ for (int i = 0; i < times.size(); i++) {
+ System.err.println("firetime = " + times.get(i));
+ }
+
+ Calendar tt = Calendar.getInstance();
+ tt.set(Calendar.DATE, 17);
+ tt.set(Calendar.MONTH, 5 - 1);
+ tt.set(Calendar.HOUR, 11);
+ tt.set(Calendar.MINUTE, 0);
+ tt.set(Calendar.SECOND, 7);
+
+ System.err.println("\nWill fire on: " + tt.getTime() + " -- " + ct.willFireOn(tt, false));
+
+
+// CRON Expression: 0 0 9 * * ?
+//
+// TimeZone.getDefault().getDisplayName() = Central African Time
+// TimeZone.getDefault().getID() = Africa/Harare
+ //
+//// System.err.println();
+//// System.err.println();
+//// System.err.println();
+//// System.err.println("Daylight test:");
+////
+//// CronTrigger trigger = new CronTrigger();
+////
+//// TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");
+//// // TimeZone timeZone = TimeZone.getDefault();
+////
+//// trigger.setTimeZone(timeZone);
+//// trigger.setCronExpression("0 0 1 ? 4 *");
+////
+//// Date start = new Date(1056319200000L);
+//// Date end = new Date(1087682399000L);
+////
+//// trigger.setStartTime(start);
+//// trigger.setEndTime(end);
+////
+//// Date next = new Date(1056232800000L);
+//// while (next != null) {
+//// next = trigger.getFireTimeAfter(next);
+//// if (next != null) {
+//// Calendar cal = Calendar.getInstance();
+//// cal.setTimeZone(timeZone);
+//// cal.setTime(next);
+//// System.err.println(cal.get(Calendar.MONTH) + "/"
+//// + cal.get(Calendar.DATE) + "/" + cal.get(Calendar.YEAR)
+//// + " - " + cal.get(Calendar.HOUR_OF_DAY) + ":"
+//// + cal.get(Calendar.MINUTE));
+//// }
+//// }
+////
+//// System.err.println();
+//// System.err.println();
+//// System.err.println();
+//// System.err.println("Midnight test:");
+////
+//// trigger = new CronTrigger();
+////
+//// timeZone = TimeZone.getTimeZone("Asia/Jerusalem");
+//// // timeZone = TimeZone.getTimeZone("America/Los_Angeles");
+//// // TimeZone timeZone = TimeZone.getDefault();
+////
+//// trigger.setTimeZone(timeZone);
+//// trigger.setCronExpression("0 /15 * ? 4 *");
+////
+//// start = new Date(1056319200000L);
+//// end = new Date(1087682399000L);
+////
+//// trigger.setStartTime(start);
+//// trigger.setEndTime(end);
+////
+//// next = new Date(1056232800000L);
+//// while (next != null) {
+//// next = trigger.getFireTimeAfter(next);
+//// if (next != null) {
+//// Calendar cal = Calendar.getInstance();
+//// cal.setTimeZone(timeZone);
+//// cal.setTime(next);
+//// System.err.println(cal.get(Calendar.MONTH) + "/"
+//// + cal.get(Calendar.DATE) + "/" + cal.get(Calendar.YEAR)
+//// + " - " + cal.get(Calendar.HOUR_OF_DAY) + ":"
+//// + cal.get(Calendar.MINUTE));
+//// }
+//// }
+
+ }
+}
+
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/InterruptableJob.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/InterruptableJob.java
new file mode 100644
index 000000000..804aba503
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/InterruptableJob.java
@@ -0,0 +1,93 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz;
+
+/**
+ *
+ * The interface to be implemented by {@link Job}s
that provide a
+ * mechanism for having their execution interrupted. It is NOT a requirment
+ * for jobs to implement this interface - in fact, for most people, none of
+ * their jobs will.
+ *
+ * The means of actually interrupting the Job must be implemented within the
+ * Job
itself (the interrupt()
method of this
+ * interface is simply a means for the scheduler to inform the Job
+ * that a request has been made for it to be interrupted). The mechanism that
+ * your jobs use to interrupt themselves might vary between implementations.
+ * However the principle idea in any implementation should be to have the
+ * body of the job's execute(..)
periodically check some flag to
+ * see if an interruption has been requested, and if the flag is set, somehow
+ * abort the performance of the rest of the job's work. An example of
+ * interrupting a job can be found in the java source for the class
+ * com.fr.third.org.quartz.examples.DumbInterruptableJob
. It is legal to use
+ * some combination of wait()
and notify()
+ * synchronization within interrupt()
and execute(..)
+ * in order to have the interrupt()
method block until the
+ * execute(..)
signals that it has noticed the set flag.
+ *
+ * If the Job performs some form of blocking I/O or similar functions, you may
+ * want to consider having the Job.execute(..)
method store a
+ * reference to the calling Thread
as a member variable. Then the
+ * impplementation of this interfaces interrupt()
method can call
+ * interrupt()
on that Thread. Before attempting this, make
+ * sure that you fully understand what java.lang.Thread.interrupt()
+ * does and doesn't do. Also make sure that you clear the Job's member
+ * reference to the Thread when the execute(..) method exits (preferrably in a
+ * finally
block.
+ *
+ * See Example 7 (com.fr.third.org.quartz.examples.example7.DumbInterruptableJob) for a simple + * implementation demonstration. + *
+ * @see Job + * @see StatefulJob + * @see Scheduler#interrupt(String, String) + * + * @author James House + */ +public interface InterruptableJob extends Job { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *
+ * Called by the {@link Scheduler}
when a user
+ * interrupts the Job
.
+ *
+ * The interface to be implemented by classes which represent a 'job' to be + * performed. + *
+ * + *
+ * Instances of Job
must have a public
+ * no-argument constructor.
+ *
+ * JobDataMap
provides a mechanism for 'instance member data'
+ * that may be required by some implementations of this interface.
+ *
+ * Called by the {@link Scheduler}
when a {@link Trigger}
+ * fires that is associated with the Job
.
+ *
+ * The implementation may wish to set a
+ * {@link JobExecutionContext#setResult(Object) result} object on the
+ * {@link JobExecutionContext} before this method exits. The result itself
+ * is meaningless to Quartz, but may be informative to
+ * {@link JobListener}s
or
+ * {@link TriggerListener}s
that are watching the job's
+ * execution.
+ *
+ * Holds state information for Job
instances.
+ *
+ * JobDataMap
instances are stored once when the Job
+ * is added to a scheduler. They are also re-persisted after every execution of
+ * StatefulJob
instances.
+ *
+ * JobDataMap
instances can also be stored with a
+ * Trigger
. This can be useful in the case where you have a Job
+ * that is stored in the scheduler for regular/repeated use by multiple
+ * Triggers, yet with each independent triggering, you want to supply the
+ * Job with different data inputs.
+ *
+ * The JobExecutionContext
passed to a Job at execution time
+ * also contains a convenience JobDataMap
that is the result
+ * of merging the contents of the trigger's JobDataMap (if any) over the
+ * Job's JobDataMap (if any).
+ *
+ * Create an empty JobDataMap
.
+ *
+ * Create a JobDataMap
with the given data.
+ *
+ * Adds the given boolean
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given Boolean
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given char
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given Character
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given double
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given Double
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given float
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given Float
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given int
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given Integer
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given long
value as a string version to the
+ * Job
's data map.
+ *
+ * Adds the given Long
value as a string version to the
+ * Job
's data map.
+ *
+ * Retrieve the identified int
value from the JobDataMap
.
+ *
+ * Retrieve the identified int
value from the JobDataMap
.
+ *
+ * Retrieve the identified int
value from the JobDataMap
.
+ *
+ * Retrieve the identified boolean
value from the JobDataMap
.
+ *
+ * Retrieve the identified boolean
value from the
+ * JobDataMap
.
+ *
+ * Retrieve the identified Boolean
value from the JobDataMap
.
+ *
+ * Retrieve the identified char
value from the JobDataMap
.
+ *
+ * Retrieve the identified Character
value from the JobDataMap
.
+ *
+ * Retrieve the identified double
value from the JobDataMap
.
+ *
+ * Retrieve the identified double
value from the JobDataMap
.
+ *
+ * Retrieve the identified Double
value from the JobDataMap
.
+ *
+ * Retrieve the identified float
value from the JobDataMap
.
+ *
+ * Retrieve the identified float
value from the JobDataMap
.
+ *
+ * Retrieve the identified Float
value from the JobDataMap
.
+ *
+ * Retrieve the identified long
value from the JobDataMap
.
+ *
+ * Retrieve the identified long
value from the JobDataMap
.
+ *
+ * Retrieve the identified Long
value from the JobDataMap
.
+ *
+ * Conveys the detail properties of a given Job
instance.
+ *
+ * Quartz does not store an actual instance of a Job
class, but
+ * instead allows you to define an instance of one, through the use of a JobDetail
.
+ *
+ * Job
s have a name and group associated with them, which
+ * should uniquely identify them within a single {@link Scheduler}
.
+ *
+ * Trigger
s are the 'mechanism' by which Job
s
+ * are scheduled. Many Trigger
s can point to the same Job
,
+ * but a single Trigger
can only point to one Job
.
+ *
+ * Create a JobDetail
with no specified name or group, and
+ * the default settings of all the other properties.
+ *
+ * Note that the {@link #setName(String)},{@link #setGroup(String)}and + * {@link #setJobClass(Class)}methods must be called before the job can be + * placed into a {@link Scheduler} + *
+ */ + public JobDetail() { + // do nothing... + } + + /** + *
+ * Create a JobDetail
with the given name, and group, and
+ * the default settings of all the other properties.
+ *
null
, Scheduler.DEFAULT_GROUP will be used.
+ *
+ * @exception IllegalArgumentException
+ * if nameis null or empty, or the group is an empty string.
+ */
+ public JobDetail(String name, String group, Class jobClass) {
+ setName(name);
+ setGroup(group);
+ setJobClass(jobClass);
+ }
+
+ /**
+ *
+ * Create a JobDetail
with the given name, and group, and
+ * the given settings of all the other properties.
+ *
null
, Scheduler.DEFAULT_GROUP will be used.
+ *
+ * @exception IllegalArgumentException
+ * if nameis null or empty, or the group is an empty string.
+ */
+ public JobDetail(String name, String group, Class jobClass,
+ boolean volatility, boolean durability, boolean recover) {
+ setName(name);
+ setGroup(group);
+ setJobClass(jobClass);
+ setVolatility(volatility);
+ setDurability(durability);
+ setRequestsRecovery(recover);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Get the name of this Job
.
+ *
+ * Set the name of this Job
.
+ *
+ * Get the group of this Job
.
+ *
+ * Set the group of this Job
.
+ *
null
, Scheduler.DEFAULT_GROUP will be used.
+ *
+ * @exception IllegalArgumentException
+ * if the group is an empty string.
+ */
+ public void setGroup(String group) {
+ if (group != null && group.trim().length() == 0) {
+ throw new IllegalArgumentException(
+ "Group name cannot be empty.");
+ }
+
+ if (group == null) {
+ group = Scheduler.DEFAULT_GROUP;
+ }
+
+ this.group = group;
+ }
+
+ /**
+ *
+ * Returns the 'full name' of the JobDetail
in the format
+ * "group.name".
+ *
+ * Return the description given to the Job
instance by its
+ * creator (if any).
+ *
+ * Set a description for the Job
instance - may be useful
+ * for remembering/displaying the purpose of the job, though the
+ * description has no meaning to Quartz.
+ *
+ * Get the instance of Job
that will be executed.
+ *
+ * Set the instance of Job
that will be executed.
+ *
Job
.
+ */
+ public void setJobClass(Class jobClass) {
+ if (jobClass == null) {
+ throw new IllegalArgumentException("Job class cannot be null.");
+ }
+
+ if (!Job.class.isAssignableFrom(jobClass)) {
+ throw new IllegalArgumentException(
+ "Job class must implement the Job interface.");
+ }
+
+ this.jobClass = jobClass;
+ }
+
+ /**
+ *
+ * Get the JobDataMap
that is associated with the Job
.
+ *
+ * Set the JobDataMap
to be associated with the Job
.
+ *
+ * Validates whether the properties of the JobDetail
are
+ * valid for submission into a Scheduler
.
+ *
+ * @throws IllegalStateException
+ * if a required property (such as Name, Group, Class) is not
+ * set.
+ */
+ public void validate() throws SchedulerException {
+ if (name == null) {
+ throw new SchedulerException("Job's name cannot be null",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+
+ if (group == null) {
+ throw new SchedulerException("Job's group cannot be null",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+
+ if (jobClass == null) {
+ throw new SchedulerException("Job's class cannot be null",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+ }
+
+ /**
+ *
+ * Set whether or not the Job
should be persisted in the
+ * {@link com.fr.third.org.quartz.spi.JobStore}
for re-use after program
+ * restarts.
+ *
+ * If not explicitly set, the default value is false
.
+ *
+ * Set whether or not the Job
should remain stored after it
+ * is orphaned (no {@link Trigger}s
point to it).
+ *
+ * If not explicitly set, the default value is false
.
+ *
+ * Set whether or not the the Scheduler
should re-execute
+ * the Job
if a 'recovery' or 'fail-over' situation is
+ * encountered.
+ *
+ * If not explicitly set, the default value is false
.
+ *
+ * Whether or not the Job
should not be persisted in the
+ * {@link com.fr.third.org.quartz.spi.JobStore}
for re-use after program
+ * restarts.
+ *
+ * If not explicitly set, the default value is false
.
+ *
true
if the Job
should be garbage
+ * collected along with the {@link Scheduler}
.
+ */
+ public boolean isVolatile() {
+ return volatility;
+ }
+
+ /**
+ *
+ * Whether or not the Job
should remain stored after it is
+ * orphaned (no {@link Trigger}s
point to it).
+ *
+ * If not explicitly set, the default value is false
.
+ *
true
if the Job should remain persisted after
+ * being orphaned.
+ */
+ public boolean isDurable() {
+ return durability;
+ }
+
+ /**
+ *
+ * Whether or not the Job
implements the interface {@link StatefulJob}
.
+ *
+ * Instructs the Scheduler
whether or not the Job
+ * should be re-executed if a 'recovery' or 'fail-over' situation is
+ * encountered.
+ *
+ * If not explicitly set, the default value is false
.
+ *
+ * Add the specified name of a {@link JobListener}
to the
+ * end of the Job
's list of listeners.
+ *
+ * Remove the specified name of a {@link JobListener}
from
+ * the Job
's list of listeners.
+ *
+ * Returns an array of String
s containing the names of all
+ * {@link JobListener}
s assigned to the Job
,
+ * in the order in which they should be notified.
+ *
+ * Return a simple string representation of this object. + *
+ */ + public String toString() { + return "JobDetail '" + getFullName() + "': jobClass: '" + + ((getJobClass() == null) ? null : getJobClass().getName()) + + " isStateful: " + isStateful() + " isVolatile: " + + isVolatile() + " isDurable: " + isDurable() + + " requestsRecovers: " + requestsRecovery(); + } + + public boolean equals(Object obj) { + if (!(obj instanceof JobDetail)) { + return false; + } + + JobDetail other = (JobDetail) obj; + + if (other.getName() == null && getName() != null) { + return false; + } + if (other.getName() != null && !other.getName().equals(getName())) { + return false; + } + + if (other.getGroup() == null && getGroup() != null) { + return false; + } + if (other.getGroup() != null && !other.getGroup().equals(getGroup())) { + return false; + } + + return true; + } + + public int hashCode() { + return getFullName().hashCode(); + } + + public Object clone() { + JobDetail copy; + try { + copy = (JobDetail) super.clone(); + copy.jobListeners = ListOrderedSet.decorate(new HashSet()); + copy.jobListeners.addAll(jobListeners); + if (jobDataMap != null) { + copy.jobDataMap = (JobDataMap) jobDataMap.clone(); + } + } catch (CloneNotSupportedException ex) { + throw new IncompatibleClassChangeError("Not Cloneable."); + } + + return copy; + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/JobExecutionContext.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/JobExecutionContext.java new file mode 100644 index 000000000..b5f47efc0 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/JobExecutionContext.java @@ -0,0 +1,368 @@ + +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz; + +import java.util.Date; +import java.util.HashMap; + +import com.fr.third.org.quartz.spi.TriggerFiredBundle; + +/** + *
+ * A context bundle containing handles to various environment information, that
+ * is given to a {@link com.fr.third.org.quartz.JobDetail}
instance as it is
+ * executed, and to a {@link Trigger}
instance after the
+ * execution completes.
+ *
+ * The JobDataMap
found on this object (via the
+ * getMergedJobDataMap()
method) serves as a convenience -
+ * it is a merge of the JobDataMap
found on the
+ * JobDetail
and the one found on the Trigger
, with
+ * the value in the latter overriding any same-named values in the former.
+ * It is thus considered a 'best practice' that the execute code of a Job
+ * retrieve data from the JobDataMap found on this object NOTE: Do not
+ * expect value 'set' into this JobDataMap to somehow be set back onto a
+ * StatefulJob
's own JobDataMap.
+ *
+ * JobExecutionContext
s are also returned from the
+ * Scheduler.getCurrentlyExecutingJobs()
+ * method. These are the same instances as those passed into the jobs that are
+ * currently executing within the scheduler. The exception to this is when your
+ * application is using Quartz remotely (i.e. via RMI) - in which case you get
+ * a clone of the JobExecutionContext
s, and their references to
+ * the Scheduler
and Job
instances have been lost (a
+ * clone of the JobDetail
is still available - just not a handle
+ * to the job instance that is running).
+ *
+ * Create a JobExcecutionContext with the given context data. + *
+ */ + public JobExecutionContext(Scheduler scheduler, + TriggerFiredBundle firedBundle, Job job) { + this.scheduler = scheduler; + this.trigger = firedBundle.getTrigger(); + this.calendar = firedBundle.getCalendar(); + this.jobDetail = firedBundle.getJobDetail(); + this.job = job; + this.recovering = firedBundle.isRecovering(); + this.fireTime = firedBundle.getFireTime(); + this.scheduledFireTime = firedBundle.getScheduledFireTime(); + this.prevFireTime = firedBundle.getPrevFireTime(); + this.nextFireTime = firedBundle.getNextFireTime(); + + this.jobDataMap = new JobDataMap(); + this.jobDataMap.putAll(jobDetail.getJobDataMap()); + this.jobDataMap.putAll(trigger.getJobDataMap()); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *
+ * Get a handle to the Scheduler
instance that fired the
+ * Job
.
+ *
+ * Get a handle to the Trigger
instance that fired the
+ * Job
.
+ *
+ * Get a handle to the Calendar
referenced by the Trigger
+ * instance that fired the Job
.
+ *
+ * If the Job
is being re-executed because of a 'recovery'
+ * situation, this method will return true
.
+ *
+ * Get the convenience JobDataMap
of this execution context.
+ *
+ * The JobDataMap
found on this object serves as a convenience -
+ * it is a merge of the JobDataMap
found on the
+ * JobDetail
and the one found on the Trigger
, with
+ * the value in the latter overriding any same-named values in the former.
+ * It is thus considered a 'best practice' that the execute code of a Job
+ * retrieve data from the JobDataMap found on this object.
+ *
NOTE: Do not
+ * expect value 'set' into this JobDataMap to somehow be set back onto a
+ * StatefulJob
's own JobDataMap.
+ *
+ * Attempts to change the contents of this map typically result in an
+ * IllegalStateException
.
+ *
+ * Get the JobDetail
associated with the Job
.
+ *
+ * Get the instance of the Job
that was created for this
+ * execution.
+ *
+ * Note: The Job instance is not available through remote scheduler + * interfaces. + *
+ */ + public Job getJobInstance() { + return job; + } + + /** + * The actual time the trigger fired. For instance the scheduled time may + * have been 10:00:00 but the actual fire time may have been 10:00:03 if + * the scheduler was too busy. + * + * @return Returns the fireTime. + * @see #getScheduledFireTime() + */ + public Date getFireTime() { + return fireTime; + } + + /** + * The scheduled time the trigger fired for. For instance the scheduled + * time may have been 10:00:00 but the actual fire time may have been + * 10:00:03 if the scheduler was too busy. + * + * @return Returns the scheduledFireTime. + * @see #getFireTime() + */ + public Date getScheduledFireTime() { + return scheduledFireTime; + } + + public Date getPreviousFireTime() { + return prevFireTime; + } + + public Date getNextFireTime() { + return nextFireTime; + } + + public String toString() { + return "JobExecutionContext:" + " trigger: '" + + getTrigger().getFullName() + " job: " + + getJobDetail().getFullName() + " fireTime: '" + getFireTime() + + " scheduledFireTime: " + getScheduledFireTime() + + " previousFireTime: '" + getPreviousFireTime() + + " nextFireTime: " + getNextFireTime() + " isRecovering: " + + isRecovering() + " refireCount: " + getRefireCount(); + } + + /** + * Returns the result (if any) that theJob
set before its
+ * execution completed (the type of object set as the result is entirely up
+ * to the particular job).
+ *
+ *
+ * The result itself is meaningless to Quartz, but may be informative
+ * to {@link JobListener}s
or
+ * {@link TriggerListener}s
that are watching the job's
+ * execution.
+ *
Job
's execution (the type of
+ * object set as the result is entirely up to the particular job).
+ *
+ *
+ * The result itself is meaningless to Quartz, but may be informative
+ * to {@link JobListener}s
or
+ * {@link TriggerListener}s
that are watching the job's
+ * execution.
+ *
JobListener
s and TriggerListener
s.
+ *
+ * @return Returns the jobRunTime.
+ */
+ public long getJobRunTime() {
+ return jobRunTime;
+ }
+
+ /**
+ * @param jobRunTime The jobRunTime to set.
+ */
+ public void setJobRunTime(long jobRunTime) {
+ this.jobRunTime = jobRunTime;
+ }
+
+ /**
+ * Put the specified value into the context's data map with the given key.
+ * Possibly useful for sharing data between listeners and jobs.
+ *
+ * NOTE: this data is volatile - it is lost after the job execution + * completes, and all TriggerListeners and JobListeners have been + * notified.
+ * + * @param key + * @param value + */ + public void put(Object key, Object value) { + data.put(key, value); + } + + /** + * Get the value with the given key from the context's data map. + * + * @param key + */ + public Object get(Object key) { + return data.get(key); + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/JobExecutionException.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/JobExecutionException.java new file mode 100644 index 000000000..9ea782370 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/JobExecutionException.java @@ -0,0 +1,182 @@ + +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz; + +/** + *
+ * An exception that can be thrown by a {@link com.fr.third.org.quartz.Job}
+ * to indicate to the Quartz {@link Scheduler}
that an error
+ * occured while executing, and whether or not the Job
requests
+ * to be re-fired immediately (using the same {@link JobExecutionContext}
,
+ * or whether it wants to be unscheduled.
+ *
+ * Note that if the flag for 'refire immediately' is set, the flags for + * unscheduling the Job are ignored. + *
+ * + * @see Job + * @see JobExecutionContext + * @see SchedulerException + * + * @author James House + */ +public class JobExecutionException extends SchedulerException { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private boolean refire = false; + + private boolean unscheduleTrigg = false; + + private boolean unscheduleAllTriggs = false; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *
+ * Create a JobExcecutionException, with the 're-fire immediately' flag set
+ * to false
.
+ *
+ * Create a JobExcecutionException, with the given cause. + *
+ */ + public JobExecutionException(Throwable cause) { + super(cause); + } + + /** + *+ * Create a JobExcecutionException, with the given message. + *
+ */ + public JobExecutionException(String msg) { + super(msg); + } + + /** + *+ * Create a JobExcecutionException with the 're-fire immediately' flag set + * to the given value. + *
+ */ + public JobExecutionException(boolean refireImmediately) { + refire = refireImmediately; + } + + /** + *+ * Create a JobExcecutionException with the given underlying exception, and + * the 're-fire immediately' flag set to the given value. + *
+ */ + public JobExecutionException(Throwable cause, boolean refireImmediately) { + super(cause); + + refire = refireImmediately; + } + + /** + *+ * Create a JobExcecutionException with the given message, and underlying + * exception. + *
+ */ + public JobExecutionException(String msg, Throwable cause) { + super(msg, cause); + } + + /** + *+ * Create a JobExcecutionException with the given message, and underlying + * exception, and the 're-fire immediately' flag set to the given value. + *
+ */ + public JobExecutionException(String msg, Throwable cause, + boolean refireImmediately) { + super(msg, cause); + + refire = refireImmediately; + } + + /** + * Create a JobExcecutionException with the given message and the 're-fire + * immediately' flag set to the given value. + */ + public JobExecutionException(String msg, boolean refireImmediately) { + super(msg); + + refire = refireImmediately; + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public void setRefireImmediately(boolean refire) { + this.refire = refire; + } + + public boolean refireImmediately() { + return refire; + } + + public void setUnscheduleFiringTrigger(boolean unscheduleTrigg) { + this.unscheduleTrigg = unscheduleTrigg; + } + + public boolean unscheduleFiringTrigger() { + return unscheduleTrigg; + } + + public void setUnscheduleAllTriggers(boolean unscheduleAllTriggs) { + this.unscheduleAllTriggs = unscheduleAllTriggs; + } + + public boolean unscheduleAllTriggers() { + return unscheduleAllTriggs; + } + +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/JobListener.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/JobListener.java new file mode 100644 index 000000000..3f66c8ff2 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/JobListener.java @@ -0,0 +1,96 @@ + +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz; + +/** + *
+ * The interface to be implemented by classes that want to be informed when a
+ * {@link com.fr.third.org.quartz.JobDetail}
executes. In general,
+ * applications that use a Scheduler
will not have use for this
+ * mechanism.
+ *
+ * Get the name of the JobListener
.
+ *
+ * Called by the {@link Scheduler}
when a {@link com.fr.third.org.quartz.JobDetail}
+ * is about to be executed (an associated {@link Trigger}
+ * has occured).
+ *
+ * This method will not be invoked if the execution of the Job was vetoed
+ * by a {@link TriggerListener}
.
+ *
+ * Called by the {@link Scheduler}
when a {@link com.fr.third.org.quartz.JobDetail}
+ * was about to be executed (an associated {@link Trigger}
+ * has occured), but a {@link TriggerListener}
vetoed it's
+ * execution.
+ *
+ * Called by the {@link Scheduler}
after a {@link com.fr.third.org.quartz.JobDetail}
+ * has been executed, and be for the associated Trigger
's
+ * triggered(xx)
method has been called.
+ *
+ * An exception that is thrown to indicate that there has been a failure in the + * scheduler's underlying persistence mechanism. + *
+ * + * @author James House + */ +public class JobPersistenceException extends SchedulerException { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *
+ * Create a JobPersistenceException
with the given message.
+ *
+ * Create a JobPersistenceException
with the given message
+ * and error code.
+ *
+ * Create a JobPersistenceException
with the given message
+ * and cause.
+ *
+ * Create a JobPersistenceException
with the given message,
+ * cause and error code.
+ *
NthIncludedDayTrigger
will skip excluded days on the
+ * associated calendar. This would commonly be used in an Nth
+ * business day situation, in which the user wishes to fire a particular job on
+ * the Nth business day (i.e. the 5th business day of
+ * every month). Each NthIncludedDayTrigger
also has an associated
+ * fireAtTime
which indicates at what time of day the trigger is
+ * to fire.
+ *
+ * All NthIncludedDayTrigger
s default to a monthly interval type
+ * (fires on the Nth day of every month) with N = 1 (first
+ * non-excluded day) and fireAtTime
set to 12:00 PM (noon). These
+ * values can be changed using the {@link #setN}, {@link #setIntervalType}, and
+ * {@link #setFireAtTime} methods. Users may also want to note the
+ * {@link #setNextFireCutoffInterval} and {@link #getNextFireCutoffInterval}
+ * methods.
+ *
+ * Take, for example, the following calendar: + *
+ *
+ * July August September + * Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa + * 1 W 1 2 3 4 5 W 1 2 W + * W H 5 6 7 8 W W 8 9 10 11 12 W W H 6 7 8 9 W + * W 11 12 13 14 15 W W 15 16 17 18 19 W W 12 13 14 15 16 W + * W 18 19 20 21 22 W W 22 23 24 25 26 W W 19 20 21 22 23 W + * W 25 26 27 28 29 W W 29 30 31 W 26 27 28 29 30 + * W + *+ *
+ * Where W's represent weekend days, and H's represent holidays, all of which
+ * are excluded on a calendar associated with an
+ * NthIncludedDayTrigger
with n=5
and
+ * intervalType=INTERVAL_TYPE_MONTHLY
. In this case, the trigger
+ * would fire on the 8th of July (because of the July 4 holiday),
+ * the 5th of August, and the 8th of September (because
+ * of Labor Day).
+ *
+ * @author Aaron Craven
+ */
+public class NthIncludedDayTrigger extends Trigger {
+
+ static final long serialVersionUID = 6267700049629328293L;
+
+ /**
+ * Instructs the Scheduler
that upon a mis-fire situation, the
+ * NthIncludedDayTrigger
wants to be fired now by the
+ * Scheduler
+ */
+ public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1;
+
+ /**
+ * Instructs the Scheduler
that upon a mis-fire situation, the
+ * NthIncludedDayTrigger
wants to have
+ * nextFireTime
updated to the next time in the schedule after
+ * the current time, but it does not want to be fired now.
+ */
+ public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2;
+
+ /**
+ * indicates a monthly trigger type (fires on the Nth included
+ * day of every month).
+ */
+ public static final int INTERVAL_TYPE_MONTHLY = 1;
+
+ /**
+ * indicates a yearly trigger type (fires on the Nth included
+ * day of every year).
+ */
+ public static final int INTERVAL_TYPE_YEARLY = 2;
+
+ /**
+ * indicates a weekly trigger type (fires on the Nth included
+ * day of every week). When using this interval type, care must be taken
+ * not to think of the value of n
as an analog to
+ * java.util.Calendar.DAY_OF_WEEK
. Such a comparison can only
+ * be drawn when there are no calendars associated with the trigger. To
+ * illustrate, consider an NthIncludedDayTrigger
with
+ * n = 3
which is associated with a Calendar excluding
+ * non-weekdays. The trigger would fire on the 3rd
+ * included day of the week, which would be 4th
+ * actual day of the week.
+ */
+ public static final int INTERVAL_TYPE_WEEKLY = 3;
+
+ private Date startTime = new Date();
+ private Date endTime;
+ private Date previousFireTime;
+ private Date nextFireTime;
+ private Calendar calendar;
+
+ private int n = 1;
+ private int intervalType = INTERVAL_TYPE_MONTHLY;
+ private int fireAtHour = 12;
+ private int fireAtMinute = 0;
+ private int fireAtSecond = 0;
+ private int nextFireCutoffInterval = 12;
+
+ private TimeZone timeZone;
+
+ /**
+ * Create an NthIncludedDayTrigger
with no specified name,
+ * group, or JobDetail
. This will result initially in a
+ * default monthly trigger that fires on the first day of every month at
+ * 12:00 PM (n
=1,
+ * intervalType={@link #INTERVAL_TYPE_MONTHLY}
,
+ * fireAtTime="12:00"
).
+ *
+ * Note that setName()
, setGroup()
,
+ * setJobName()
, and setJobGroup()
, must be
+ * called before the NthIncludedDayTrigger
can be placed into
+ * a Scheduler
.
+ */
+ public NthIncludedDayTrigger() {
+ super();
+ }
+
+ /**
+ * Create an NthIncludedDayTrigger
with the given name and
+ * group but no specified JobDetail
. This will result
+ * initially in a default monthly trigger that fires on the first day of
+ * every month at 12:00 PM (n
=1,
+ * intervalType={@link #INTERVAL_TYPE_MONTHLY}
,
+ * fireAtTime="12:00"
).
+ *
+ * Note that setJobName()
and setJobGroup()
must
+ * be called before the NthIncludedDayTrigger
can be placed
+ * into a Scheduler
.
+ *
+ * @param name the name for the NthIncludedDayTrigger
+ * @param group the group for the NthIncludedDayTrigger
+ */
+ public NthIncludedDayTrigger(String name, String group) {
+ super(name, group);
+ }
+
+ /**
+ * Create an NthIncludedDayTrigger
with the given name and
+ * group and the specified JobDetail
. This will result
+ * initially in a default monthly trigger that fires on the first day of
+ * every month at 12:00 PM (n
=1,
+ * intervalType={@link #INTERVAL_TYPE_MONTHLY}
,
+ * fireAtTime="12:00"
).
+ *
+ * @param name the name for the NthIncludedDayTrigger
+ * @param group the group for the NthIncludedDayTrigger
+ * @param jobName the name of the job to associate with the
+ * NthIncludedDayTrigger
+ * @param jobGroup the group containing the job to associate with the
+ * NthIncludedDayTrigger
+ */
+ public NthIncludedDayTrigger(String name, String group, String jobName,
+ String jobGroup) {
+ super(name, group, jobName, jobGroup);
+ }
+
+ /**
+ * Sets the day of the interval on which the
+ * NthIncludedDayTrigger
should fire. If the Nth
+ * day of the interval does not exist (i.e. the 32nd of a
+ * month), the trigger simply will never fire. N may not be less than 1.
+ *
+ * @param n the day of the interval on which the trigger should fire.
+ * @throws java.lang.IllegalArgumentException
+ * the value entered for N was not valid (probably less than or
+ * equal to zero).
+ * @see #getN()
+ */
+ public void setN(int n) {
+ if (n > 0) {
+ this.n = n;
+ } else {
+ throw new IllegalArgumentException("N must be greater than 0.");
+ }
+ }
+
+ /**
+ * Returns the day of the interval on which the
+ * NthIncludedDayTrigger
should fire.
+ *
+ * @return the value of n
+ * @see #setN(int)
+ */
+ public int getN() {
+ return this.n;
+ }
+
+ /**
+ * Sets the interval type for the NthIncludedDayTrigger
. If
+ * {@link #INTERVAL_TYPE_MONTHLY}, the trigger will fire on the
+ * Nth included day of every month. If
+ * {@link #INTERVAL_TYPE_YEARLY}, the trigger will fire on the
+ * Nth included day of every year. If
+ * {@link #INTERVAL_TYPE_WEEKLY}, the trigger will fire on the
+ * Nth included day of every week.
+ *
+ * @param intervalType the interval type for the trigger
+ * @throws java.lang.IllegalArgumentException
+ * the value of intervalType
is not valid. Valid
+ * values are represented by the INTERVAL_TYPE_WEEKLY,
+ * INTERVAL_TYPE_MONTHLY and INTERVAL_TYPE_YEARLY constants.
+ * @see #getIntervalType()
+ * @see #INTERVAL_TYPE_WEEKLY
+ * @see #INTERVAL_TYPE_MONTHLY
+ * @see #INTERVAL_TYPE_YEARLY
+ */
+ public void setIntervalType(int intervalType) {
+ switch (intervalType) {
+ case INTERVAL_TYPE_WEEKLY:
+ this.intervalType = intervalType;
+ break;
+ case INTERVAL_TYPE_MONTHLY:
+ this.intervalType = intervalType;
+ break;
+ case INTERVAL_TYPE_YEARLY:
+ this.intervalType = intervalType;
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid Interval Type:"
+ + intervalType);
+ }
+ }
+
+ /**
+ * Returns the interval type for the NthIncludedDayTrigger
.
+ *
+ * @return the trigger's interval type
+ * @see #setIntervalType(int)
+ * @see #INTERVAL_TYPE_WEEKLY
+ * @see #INTERVAL_TYPE_MONTHLY
+ * @see #INTERVAL_TYPE_YEARLY
+ */
+ public int getIntervalType() {
+ return this.intervalType;
+ }
+
+ /**
+ * Sets the fire time for the NthIncludedDayTrigger
, which
+ * should be represented as a string with the format
+ * "HH:MM[:SS]", with HH representing the 24-hour clock hour
+ * of the fire time. Hours can be represented as either a one-digit or
+ * two-digit number. Seconds are optional.
+ *
+ * @param fireAtTime the time at which the trigger should fire
+ * @throws java.lang.IllegalArgumentException
+ * the specified value for fireAtTime
could not be
+ * successfully parsed into a valid time of day.
+ * @see #getFireAtTime()
+ */
+ public void setFireAtTime(String fireAtTime) {
+ int newFireHour;
+ int newFireMinute;
+ int newFireSecond = 0;
+
+ try {
+ int i = fireAtTime.indexOf(":");
+ newFireHour = Integer.parseInt(fireAtTime.substring(0, i));
+ newFireMinute = Integer.parseInt(fireAtTime.substring(i+1, i+3));
+ i = fireAtTime.indexOf(":", i+1);
+ if (i > -1) {
+ newFireSecond = Integer.parseInt(fireAtTime.substring(i+1));
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ "Could not parse time expression '" + fireAtTime + "':" + e.getMessage());
+ }
+
+ // Check ranges
+ if ((newFireHour < 0) || (newFireHour > 23)) {
+ throw new IllegalArgumentException(
+ "Could not parse time expression '" + fireAtTime + "':" +
+ "fireAtHour must be between 0 and 23");
+ } else if ((newFireMinute < 0) || (newFireMinute > 59)) {
+ throw new IllegalArgumentException(
+ "Could not parse time expression '" + fireAtTime + "':" +
+ "fireAtMinute must be between 0 and 59");
+ } else if ((newFireSecond < 0) || (newFireSecond > 59)) {
+ throw new IllegalArgumentException(
+ "Could not parse time expression '" + fireAtTime + "':" +
+ "fireAtMinute must be between 0 and 59");
+ }
+
+ fireAtHour = newFireHour;
+ fireAtMinute = newFireMinute;
+ fireAtSecond = newFireSecond;
+ }
+
+ /**
+ * Returns the fire time for the NthIncludedDayTrigger
as a
+ * string with the format "HH:MM[:SS]", with HH representing the
+ * 24-hour clock hour of the fire time. Seconds are optional and their
+ * inclusion depends on whether or not they were provided to
+ * {@link #setFireAtTime(String)}.
+ *
+ * @return the fire time for the trigger
+ * @see #setFireAtTime(String)
+ */
+ public String getFireAtTime() {
+ NumberFormat format = NumberFormat.getNumberInstance();
+ format.setMaximumIntegerDigits(2);
+ format.setMinimumIntegerDigits(2);
+ format.setMaximumFractionDigits(0);
+
+ return format.format(this.fireAtHour) + ":" +
+ format.format(this.fireAtMinute) + ":" +
+ format.format(this.fireAtSecond);
+ }
+
+ /**
+ * Sets the nextFireCutoffInterval
for the
+ * NthIncludedDayTrigger
.
+ *
+ * Because of the conceptual design of NthIncludedDayTrigger
,
+ * it is not always possible to decide with certainty that the trigger
+ * will never fire again. Therefore, it will search for the next
+ * fire time up to a given cutoff. These cutoffs can be changed by using the
+ * {@link #setNextFireCutoffInterval(int)} and
+ * {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
+ * of the intervals specified by {@link #getIntervalType()
+ * intervalType}
.
+ *
+ * In most cases, the default value of this setting (12) is sufficient (it + * is highly unlikely, for example, that you will need to look at more than + * 12 months of dates to ensure that your trigger will never fire again). + * However, this setting is included to allow for the rare exceptions where + * this might not be true. + *
+ * For example, if your trigger is associated with a calendar that excludes
+ * a great many dates in the next 12 months, and hardly any following that,
+ * it is possible (if n
is large enough) that you could run
+ * into this situation.
+ *
+ * @param nextFireCutoffInterval the desired cutoff interval
+ * @see #getNextFireCutoffInterval()
+ * @see #getNextFireTime()
+ */
+ public void setNextFireCutoffInterval(int nextFireCutoffInterval) {
+ this.nextFireCutoffInterval = nextFireCutoffInterval;
+ }
+
+ /**
+ * Returns the nextFireCutoffInterval
for the
+ * NthIncludedDayTrigger
.
+ *
+ * Because of the conceptual design of NthIncludedDayTrigger
,
+ * it is not always possible to decide with certainty that the trigger
+ * will never fire again. Therefore, it will search for the next
+ * fire time up to a given cutoff. These cutoffs can be changed by using the
+ * {@link #setNextFireCutoffInterval(int)} and
+ * {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
+ * of the intervals specified by {@link #getIntervalType()
+ * intervalType}
.
+ *
+ * @return the chosen cutoff interval
+ * @see #setNextFireCutoffInterval(int)
+ * @see #getNextFireTime()
+ */
+ public int getNextFireCutoffInterval() {
+ return this.nextFireCutoffInterval;
+ }
+
+ /**
+ * Sets the date/time on which the trigger may begin firing. This defines
+ * the initial boundary for trigger firings — the trigger will not
+ * fire prior to this date and time. Defaults to the current date and time
+ * when the NthIncludedDayTrigger
is created.
+ *
+ * @param startTime the initial boundary for trigger firings
+ * @throws java.lang.IllegalArgumentException
+ * the specified start time is after the current end time or is
+ * null
+ * @see #getStartTime()
+ */
+ public void setStartTime(Date startTime) {
+ if (startTime == null) {
+ throw new IllegalArgumentException("Start time may not be null");
+ }
+ if ((this.endTime != null) && endTime.before(startTime)) {
+ throw new IllegalArgumentException("Start time must be before end time.");
+ }
+ this.startTime = startTime;
+ }
+
+ /**
+ * Returns the date/time on which the trigger may begin firing. This
+ * defines the initial boundary for trigger firings — the trigger
+ * will not fire prior to this date and time.
+ *
+ * @return the initial date/time on which the trigger may begin firing
+ * @see #setStartTime(Date)
+ */
+ public Date getStartTime() {
+ return this.startTime;
+ }
+
+ /**
+ * Sets the date/time on which the trigger must stop firing. This defines
+ * the final boundary for trigger firings — the trigger will not
+ * fire after to this date and time. If this value is null, no end time
+ * boundary is assumed, and the trigger can continue indefinitely.
+ *
+ * @param endTime the final boundary for trigger firings
+ * @throws java.lang.IllegalArgumentException
+ * the specified end time is before the current start time
+ * @see #getEndTime()
+ */
+ public void setEndTime(Date endTime) {
+ if ((endTime != null) && endTime.before(startTime)) {
+ throw new IllegalArgumentException("End time must be after start time.");
+ }
+ this.endTime = endTime;
+ }
+
+ /**
+ * Returns the date/time on which the trigger must stop firing. This
+ * defines the final boundary for trigger firings — the trigger will
+ * not fire after to this date and time. If this value is null, no end time
+ * boundary is assumed, and the trigger can continue indefinitely.
+ *
+ * @return the date/time on which the trigger must stop firing
+ * @see #setEndTime(Date)
+ */
+ public Date getEndTime() {
+ return this.endTime;
+ }
+
+ /**
+ * Sets the time zone in which the fireAtTime
will be resolved.
+ * If no time zone is provided, then the default time zone will be used.
+ *
+ * @see TimeZone#getDefault()
+ * @see #getTimeZone()
+ * @see #getFireAtTime()
+ * @see #setFireAtTime(String)
+ */
+ public void setTimeZone(TimeZone timeZone) {
+ this.timeZone = timeZone;
+ }
+
+ /**
+ * Gets the time zone in which the fireAtTime
will be resolved.
+ * If no time zone was explicitly set, then the default time zone is used.
+ *
+ * @see TimeZone#getDefault()
+ * @see #getTimeZone()
+ * @see #getFireAtTime()
+ * @see #setFireAtTime(String)
+ */
+ public TimeZone getTimeZone() {
+ if (timeZone == null) {
+ timeZone = TimeZone.getDefault();
+ }
+ return timeZone;
+ }
+
+
+ /**
+ * Returns the next time at which the NthIncludedDayTrigger
+ * will fire. If the trigger will not fire again, null
will be
+ * returned.
+ *
+ * Because of the conceptual design of NthIncludedDayTrigger
,
+ * it is not always possible to decide with certainty that the trigger
+ * will never fire again. Therefore, it will search for the next
+ * fire time up to a given cutoff. These cutoffs can be changed by using the
+ * {@link #setNextFireCutoffInterval(int)} and
+ * {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
+ * of the intervals specified by {@link #getIntervalType()
+ * intervalType}
.
+ *
+ * The returned value is not guaranteed to be valid until after
+ * the trigger has been added to the scheduler.
+ *
+ * @return the next fire time for the trigger
+ * @see #getNextFireCutoffInterval()
+ * @see #setNextFireCutoffInterval(int)
+ * @see #getFireTimeAfter(Date)
+ */
+ public Date getNextFireTime() {
+ return this.nextFireTime;
+ }
+
+ /**
+ * Returns the previous time at which the
+ * NthIncludedDayTrigger
fired. If the trigger has not yet
+ * fired, null
will be returned.
+ *
+ * @return the previous fire time for the trigger
+ */
+ public Date getPreviousFireTime() {
+ return this.previousFireTime;
+ }
+
+ /**
+ * Returns the first time the NthIncludedDayTrigger
will fire
+ * after the specified date.
+ *
+ * Because of the conceptual design of NthIncludedDayTrigger
,
+ * it is not always possible to decide with certainty that the trigger
+ * will never fire again. Therefore, it will search for the next
+ * fire time up to a given cutoff. These cutoffs can be changed by using the
+ * {@link #setNextFireCutoffInterval(int)} and
+ * {@link #getNextFireCutoffInterval()} methods. The default cutoff is 12
+ * of the intervals specified by {@link #getIntervalType()
+ * intervalType}
.
+ *
+ * Therefore, for triggers with intervalType =
+ * {@link NthIncludedDayTrigger#INTERVAL_TYPE_WEEKLY
+ * INTERVAL_TYPE_WEEKLY}
, if the trigger will not fire within 12
+ * weeks after the given date/time, null
will be returned. For
+ * triggers with intervalType =
+ * {@link NthIncludedDayTrigger#INTERVAL_TYPE_MONTHLY
+ * INTERVAL_TYPE_MONTHLY}
, if the trigger will not fire within 12
+ * months after the given date/time, null
will be returned.
+ * For triggers with intervalType =
+ * {@link NthIncludedDayTrigger#INTERVAL_TYPE_YEARLY
+ * INTERVAL_TYPE_YEARLY}
, if the trigger will not fire within 12
+ * years after the given date/time, null
will be returned. In
+ * all cases, if the trigger will not fire before endTime
,
+ * null
will be returned.
+ *
+ * @param afterTime The time after which to find the nearest fire time.
+ * This argument is treated as exclusive — that is,
+ * if afterTime is a valid fire time for the trigger, it
+ * will not be returned as the next fire time.
+ * @return the first time the trigger will fire following the specified
+ * date
+ */
+ public Date getFireTimeAfter(Date afterTime) {
+ if (afterTime == null) {
+ afterTime = new Date();
+ }
+
+ if (afterTime.before(this.startTime)) {
+ afterTime = new Date(startTime.getTime() - 1000l);
+ }
+
+ if (this.intervalType == INTERVAL_TYPE_WEEKLY) {
+ return getWeeklyFireTimeAfter(afterTime);
+ } else if (this.intervalType == INTERVAL_TYPE_MONTHLY) {
+ return getMonthlyFireTimeAfter(afterTime);
+ } else if (this.intervalType == INTERVAL_TYPE_YEARLY) {
+ return getYearlyFireTimeAfter(afterTime);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the last time the NthIncludedDayTrigger
will fire.
+ * If the trigger will not fire at any point between startTime
+ * and endTime
, or there is not endTime
,
+ * null
will be returned.
+ *
+ * @return the last time the trigger will fire, or null if there is no
+ * last time.
+ */
+ public Date getFinalFireTime() {
+ if(endTime == null)
+ return null;
+
+ Date finalTime = null;
+ java.util.Calendar currCal = java.util.Calendar.getInstance();
+ currCal.setTime(this.endTime);
+
+ while ((finalTime == null)
+ && (this.startTime.before(currCal.getTime()))) {
+ currCal.add(java.util.Calendar.DATE, -1);
+
+ finalTime = getFireTimeAfter(currCal.getTime());
+ }
+
+ return finalTime;
+ }
+
+ /**
+ * Called when the Scheduler
has decided to 'fire' the trigger
+ * (execute the associated Job
), in order to give the
+ * Trigger
a chance to update itself for its next triggering
+ * (if any).
+ */
+ public void triggered(Calendar calendar) {
+ this.calendar = calendar;
+ this.previousFireTime = this.nextFireTime;
+ this.nextFireTime = getFireTimeAfter(this.nextFireTime);
+ }
+
+ /**
+ * Called by the scheduler at the time a Trigger
is first
+ * added to the scheduler, in order to have the Trigger
+ * compute its first fire time, based on any associated calendar.
+ *
+ * After this method has been called, getNextFireTime()
+ * should return a valid answer.
+ *
Trigger
will be fired
+ * by the scheduler, which is also the same value
+ * {@link #getNextFireTime()} will return (until after the first
+ * firing of the Trigger
).
+ */
+ public Date computeFirstFireTime(Calendar calendar) {
+ this.calendar = calendar;
+ this.nextFireTime =
+ getFireTimeAfter(new Date(this.startTime.getTime() - 1000l));
+
+ return this.nextFireTime;
+ }
+
+ /**
+ * Called after the Scheduler
has executed the
+ * JobDetail
associated with the Trigger
in order
+ * to get the final instruction code from the trigger.
+ *
+ * @param jobCtx the JobExecutionContext
that was used by the
+ * Job
's execute()
method.
+ * @param result the JobExecutionException
thrown by the
+ * Job
, if any (may be null
)
+ * @return one of the Trigger.INSTRUCTION_XXX constants.
+ */
+ public int executionComplete(JobExecutionContext jobCtx,
+ JobExecutionException result) {
+ if (result != null && result.refireImmediately()) {
+ return INSTRUCTION_RE_EXECUTE_JOB;
+ }
+
+ if (result != null && result.unscheduleFiringTrigger()) {
+ return INSTRUCTION_SET_TRIGGER_COMPLETE;
+ }
+
+ if (result != null && result.unscheduleAllTriggers()) {
+ return INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE;
+ }
+
+ if (!mayFireAgain()) {
+ return INSTRUCTION_DELETE_TRIGGER;
+ }
+
+ return INSTRUCTION_NOOP;
+ }
+
+ /**
+ * Used by the Scheduler
to determine whether or not it is
+ * possible for this Trigger
to fire again.
+ *
+ * If the returned value is false
then the
+ * Scheduler
may remove the Trigger
from the
+ * JobStore
+ *
+ * @return a boolean indicator of whether the trigger could potentially fire
+ * again
+ */
+ public boolean mayFireAgain() {
+ return (getNextFireTime() != null);
+ }
+
+ /**
+ * Indicates whether misfireInstruction
is a valid misfire
+ * instruction for this Trigger
.
+ *
+ * @return whether misfireInstruction
is valid.
+ */
+ protected boolean validateMisfireInstruction(int misfireInstruction) {
+ if ((misfireInstruction == MISFIRE_INSTRUCTION_SMART_POLICY) ||
+ (misfireInstruction == MISFIRE_INSTRUCTION_DO_NOTHING) ||
+ (misfireInstruction == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Updates the NthIncludedDayTrigger
's state based on the
+ * MISFIRE_INSTRUCTION_XXX that was selected when the
+ * NthIncludedDayTrigger
was created
+ *
+ * If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY,
+ * then the instruction will be interpreted as
+ * {@link #MISFIRE_INSTRUCTION_FIRE_ONCE_NOW}.
+ *
+ * @param calendar a new or updated calendar to use for the trigger
+ */
+ public void updateAfterMisfire(Calendar calendar) {
+ int instruction = getMisfireInstruction();
+
+ this.calendar = calendar;
+
+ if (instruction == MISFIRE_INSTRUCTION_SMART_POLICY) {
+ instruction = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
+ }
+
+ if (instruction == MISFIRE_INSTRUCTION_DO_NOTHING) {
+ this.nextFireTime = getFireTimeAfter(new Date());
+ } else if (instruction == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) {
+ this.nextFireTime = new Date();
+ }
+ }
+
+ /**
+ * Updates the NthIncludedDayTrigger
's state based on the
+ * given new version of the associated Calendar
.
+ *
+ * @param calendar a new or updated calendar to use for the trigger
+ * @param misfireThreshold the amount of time (in milliseconds) that must
+ * be between "now" and the time the next
+ * firing of the trigger is supposed to occur.
+ */
+ public void updateWithNewCalendar(Calendar calendar,
+ long misfireThreshold) {
+ Date now = new Date();
+ long diff;
+
+ this.calendar = calendar;
+ this.nextFireTime = getFireTimeAfter(this.previousFireTime);
+
+ if ((this.nextFireTime != null) && (this.nextFireTime.before(now))) {
+ diff = now.getTime() - this.nextFireTime.getTime();
+ if (diff >= misfireThreshold) {
+ this.nextFireTime = getFireTimeAfter(this.nextFireTime);
+ }
+ }
+ }
+
+ /**
+ * Calculates the first time an NthIncludedDayTrigger
with
+ * intervalType = {@link #INTERVAL_TYPE_WEEKLY}
will fire
+ * after the specified date. See {@link #getNextFireTime} for more
+ * information.
+ *
+ * @param afterDate The time after which to find the nearest fire time.
+ * This argument is treated as exclusive — that is,
+ * if afterTime is a valid fire time for the trigger, it
+ * will not be returned as the next fire time.
+ * @return the first time the trigger will fire following the specified
+ * date
+ */
+ private Date getWeeklyFireTimeAfter(Date afterDate) {
+ int currN = 0;
+ java.util.Calendar afterCal;
+ java.util.Calendar currCal;
+ int currWeek;
+ int weekCount = 0;
+ boolean gotOne = false;
+
+ afterCal = java.util.Calendar.getInstance(getTimeZone());
+ afterCal.setTime(afterDate);
+
+ currCal = java.util.Calendar.getInstance(getTimeZone());
+ currCal.set(afterCal.get(java.util.Calendar.YEAR),
+ afterCal.get(java.util.Calendar.MONTH),
+ afterCal.get(java.util.Calendar.DAY_OF_MONTH));
+
+ //move to the first day of the week
+ while (currCal.get(java.util.Calendar.DAY_OF_WEEK) !=
+ currCal.getFirstDayOfWeek()) {
+ currCal.add(java.util.Calendar.DAY_OF_MONTH, -1);
+ }
+
+ currCal.set(java.util.Calendar.HOUR_OF_DAY, this.fireAtHour);
+ currCal.set(java.util.Calendar.MINUTE, this.fireAtMinute);
+ currCal.set(java.util.Calendar.SECOND, this.fireAtSecond);
+ currCal.set(java.util.Calendar.MILLISECOND, 0);
+
+ currWeek = currCal.get(java.util.Calendar.WEEK_OF_YEAR);
+
+ while ((!gotOne) && (weekCount < this.nextFireCutoffInterval)) {
+ while ((currN != this.n) && (weekCount < 12)) {
+ //if we move into a new week, reset the current "n" counter
+ if (currCal.get(java.util.Calendar.WEEK_OF_YEAR) != currWeek) {
+ currN = 0;
+ weekCount++;
+ currWeek = currCal.get(java.util.Calendar.WEEK_OF_YEAR);
+ }
+
+ //treating a null calendar as an all-inclusive calendar,
+ // increment currN if the current date being tested is included
+ // on the calendar
+ if ((calendar == null)
+ || (calendar.isTimeIncluded(currCal.getTime().getTime()))) {
+ currN++;
+ }
+
+ if (currN != this.n) {
+ currCal.add(java.util.Calendar.DATE, 1);
+ }
+
+ //if we pass endTime, drop out and return null.
+ if ((this.endTime != null)
+ && (currCal.getTime().after(this.endTime))) {
+ return null;
+ }
+ }
+
+ //We found an "n" or we've checked the requisite number of weeks.
+ // If we've found an "n", is it the right one? -- that is, we could
+ // be looking at an nth day PRIOR to afterDate
+ if (currN == this.n) {
+ if (afterDate.before(currCal.getTime())) {
+ gotOne = true;
+ } else { //resume checking on the first day of the next week
+
+ //move back to the beginning of the week and add 7 days
+ while (currCal.get(java.util.Calendar.DAY_OF_WEEK) !=
+ currCal.getFirstDayOfWeek()) {
+ currCal.add(java.util.Calendar.DAY_OF_MONTH, -1);
+ }
+ currCal.add(java.util.Calendar.DAY_OF_MONTH, 7);
+
+ currN = 0;
+ }
+ }
+ }
+
+ if (weekCount < this.nextFireCutoffInterval) {
+ return currCal.getTime();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Calculates the first time an NthIncludedDayTrigger
with
+ * intervalType = {@link #INTERVAL_TYPE_MONTHLY}
will fire
+ * after the specified date. See {@link #getNextFireTime} for more
+ * information.
+ *
+ * @param afterDate The time after which to find the nearest fire time.
+ * This argument is treated as exclusive — that is,
+ * if afterTime is a valid fire time for the trigger, it
+ * will not be returned as the next fire time.
+ * @return the first time the trigger will fire following the specified
+ * date
+ */
+ private Date getMonthlyFireTimeAfter(Date afterDate) {
+ int currN = 0;
+ java.util.Calendar afterCal;
+ java.util.Calendar currCal;
+ int currMonth;
+ int monthCount = 0;
+ boolean gotOne = false;
+
+ afterCal = java.util.Calendar.getInstance(getTimeZone());
+ afterCal.setTime(afterDate);
+
+ currCal = java.util.Calendar.getInstance(getTimeZone());
+ currCal.set(afterCal.get(java.util.Calendar.YEAR),
+ afterCal.get(java.util.Calendar.MONTH), 1);
+ currCal.set(java.util.Calendar.HOUR_OF_DAY, this.fireAtHour);
+ currCal.set(java.util.Calendar.MINUTE, this.fireAtMinute);
+ currCal.set(java.util.Calendar.SECOND, this.fireAtSecond);
+ currCal.set(java.util.Calendar.MILLISECOND, 0);
+
+ currMonth = currCal.get(java.util.Calendar.MONTH);
+
+ while ((!gotOne) && (monthCount < this.nextFireCutoffInterval)) {
+ while ((currN != this.n) && (monthCount < 12)) {
+ //if we move into a new month, reset the current "n" counter
+ if (currCal.get(java.util.Calendar.MONTH) != currMonth) {
+ currN = 0;
+ monthCount++;
+ currMonth = currCal.get(java.util.Calendar.MONTH);
+ }
+
+ //treating a null calendar as an all-inclusive calendar,
+ // increment currN if the current date being tested is included
+ // on the calendar
+ if ((calendar == null)
+ || (calendar.isTimeIncluded(currCal.getTime().getTime()))) {
+ currN++;
+ }
+
+ if (currN != this.n) {
+ currCal.add(java.util.Calendar.DATE, 1);
+ }
+
+ //if we pass endTime, drop out and return null.
+ if ((this.endTime != null)
+ && (currCal.getTime().after(this.endTime))) {
+ return null;
+ }
+ }
+
+ //We found an "n" or we've checked the requisite number of months.
+ // If we've found an "n", is it the right one? -- that is, we could
+ // be looking at an nth day PRIOR to afterDate
+ if (currN == this.n) {
+ if (afterDate.before(currCal.getTime())) {
+ gotOne = true;
+ } else { //resume checking on the first day of the next month
+ currCal.set(java.util.Calendar.DAY_OF_MONTH, 1);
+ currCal.add(java.util.Calendar.MONTH, 1);
+ currN = 0;
+ }
+ }
+ }
+
+ if (monthCount < this.nextFireCutoffInterval) {
+ return currCal.getTime();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Calculates the first time an NthIncludedDayTrigger
with
+ * intervalType = {@link #INTERVAL_TYPE_YEARLY}
will fire
+ * after the specified date. See {@link #getNextFireTime} for more
+ * information.
+ *
+ * @param afterDate The time after which to find the nearest fire time.
+ * This argument is treated as exclusive — that is,
+ * if afterTime is a valid fire time for the trigger, it
+ * will not be returned as the next fire time.
+ * @return the first time the trigger will fire following the specified
+ * date
+ */
+ private Date getYearlyFireTimeAfter(Date afterDate) {
+ int currN = 0;
+ java.util.Calendar afterCal;
+ java.util.Calendar currCal;
+ int currYear;
+ int yearCount = 0;
+ boolean gotOne = false;
+
+ afterCal = java.util.Calendar.getInstance(getTimeZone());
+ afterCal.setTime(afterDate);
+
+ currCal = java.util.Calendar.getInstance(getTimeZone());
+ currCal.set(afterCal.get(java.util.Calendar.YEAR),
+ java.util.Calendar.JANUARY, 1);
+ currCal.set(java.util.Calendar.HOUR_OF_DAY, this.fireAtHour);
+ currCal.set(java.util.Calendar.MINUTE, this.fireAtMinute);
+ currCal.set(java.util.Calendar.SECOND, this.fireAtSecond);
+ currCal.set(java.util.Calendar.MILLISECOND, 0);
+
+ currYear = currCal.get(java.util.Calendar.YEAR);
+
+ while ((!gotOne) && (yearCount < this.nextFireCutoffInterval)) {
+ while ((currN != this.n) && (yearCount < 5)) {
+ //if we move into a new year, reset the current "n" counter
+ if (currCal.get(java.util.Calendar.YEAR) != currYear) {
+ currN = 0;
+ yearCount++;
+ currYear = currCal.get(java.util.Calendar.YEAR);
+ }
+
+ //treating a null calendar as an all-inclusive calendar,
+ // increment currN if the current date being tested is included
+ // on the calendar
+ if ((calendar == null)
+ || (calendar.isTimeIncluded(currCal.getTime().getTime()))) {
+ currN++;
+ }
+
+ if (currN != this.n) {
+ currCal.add(java.util.Calendar.DATE, 1);
+ }
+
+ //if we pass endTime, drop out and return null.
+ if ((this.endTime != null)
+ && (currCal.getTime().after(this.endTime))) {
+ return null;
+ }
+ }
+
+ //We found an "n" or we've checked the requisite number of years.
+ // If we've found an "n", is it the right one? -- that is, we
+ // could be looking at an nth day PRIOR to afterDate
+ if (currN == this.n) {
+ if (afterDate.before(currCal.getTime())) {
+ gotOne = true;
+ } else { //resume checking on the first day of the next year
+ currCal.set(java.util.Calendar.DAY_OF_MONTH, 1);
+ currCal.set(java.util.Calendar.MONTH,
+ java.util.Calendar.JANUARY);
+ currCal.add(java.util.Calendar.YEAR, 1);
+ currN = 0;
+ }
+ }
+ }
+
+ if (yearCount < this.nextFireCutoffInterval) {
+ return currCal.getTime();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ObjectAlreadyExistsException.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ObjectAlreadyExistsException.java
new file mode 100644
index 000000000..05b07b7bd
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ObjectAlreadyExistsException.java
@@ -0,0 +1,89 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz;
+
+/**
+ *
+ * An exception that is thrown to indicate that an attempt to store a new
+ * object (i.e. {@link com.fr.third.org.quartz.JobDetail}
,{@link Trigger}
+ * or {@link Calendar}
) in a {@link Scheduler}
+ * failed, because one with the same name & group already exists.
+ *
+ * Create a ObjectAlreadyExistsException
with the given
+ * message.
+ *
+ * Create a ObjectAlreadyExistsException
and auto-generate a
+ * message using the name/group from the given JobDetail
.
+ *
+ * The message will read:
"Unable to store Job with name: '__' and
+ * group: '__', because one already exists with this identification."
+ *
+ * Create a ObjectAlreadyExistsException
and auto-generate a
+ * message using the name/group from the given Trigger
.
+ *
+ * The message will read:
"Unable to store Trigger with name: '__' and
+ * group: '__', because one already exists with this identification."
+ *
+ * This is the main interface of a Quartz Scheduler. + *
+ * + *
+ * A Scheduler
maintains a registery of {@link com.fr.third.org.quartz.JobDetail}
+ * s and {@link Trigger}
s. Once registered, the Scheduler
+ * is responible for executing Job
s when their associated
+ * Trigger
s fire (when their scheduled time arrives).
+ *
+ * Scheduler
instances are produced by a {@link SchedulerFactory}
.
+ * A scheduler that has already been created/initialized can be found and used
+ * through the same factory that produced it. After a Scheduler
+ * has been created, it is in "stand-by" mode, and must have its
+ * start()
method called before it will fire any Job
s.
+ *
+ * Job
s are to be created by the 'client program', by defining
+ * a class that implements the {@link com.fr.third.org.quartz.Job}
+ * interface. {@link JobDetail}
objects are then created (also
+ * by the client) to define a individual instances of the Job
.
+ * JobDetail
instances can then be registered with the Scheduler
+ * via the scheduleJob(JobDetail, Trigger)
or addJob(JobDetail, boolean)
+ * method.
+ *
+ * Trigger
s can then be defined to fire individual Job
+ * instances based on given schedules. SimpleTrigger
s are most
+ * useful for one-time firings, or firing at an exact moment in time, with N
+ * repeats with a given delay between them. CronTrigger
s allow
+ * scheduling based on time of day, day of week, day of month, and month of
+ * year.
+ *
+ * Job
s and Trigger
s have a name and group
+ * associated with them, which should uniquely identify them within a single
+ * {@link Scheduler}
. The 'group' feature may be useful for
+ * creating logical groupings or categorizations of Jobs
s and
+ * Triggers
s. If you don't have need for assigning a group to a
+ * given Jobs
of Triggers
, then you can use the
+ * DEFAULT_GROUP
constant defined on this interface.
+ *
+ * Stored Job
s can also be 'manually' triggered through the use
+ * of the triggerJob(String jobName, String jobGroup)
function.
+ *
+ * Client programs may also be interested in the 'listener' interfaces that are
+ * available from Quartz. The {@link JobListener}
interface
+ * provides notifications of Job
executions. The {@link TriggerListener}
+ * interface provides notifications of Trigger
firings. The
+ * {@link SchedulerListener}
interface provides notifications of
+ * Scheduler
events and errors.
+ *
+ * The setup/configuration of a Scheduler
instance is very
+ * customizable. Please consult the documentation distributed with Quartz.
+ *
+ * A (possibly) usefull constant that can be used for specifying the group
+ * that Job
and Trigger
instances belong to.
+ *
+ * A constant Trigger
group name used internally by the
+ * scheduler - clients should not use the value of this constant
+ * ("MANUAL_TRIGGER") for the name of a Trigger
's group.
+ *
+ * A constant Trigger
group name used internally by the
+ * scheduler - clients should not use the value of this constant
+ * ("RECOVERING_JOBS") for the name of a Trigger
's group.
+ *
+ * A constant Trigger
group name used internally by the
+ * scheduler - clients should not use the value of this constant
+ * ("FAILED_OVER_JOBS") for the name of a Trigger
's group.
+ *
JobDataMap
key that can be used to retrieve the
+ * name of the original Trigger
from a recovery trigger's
+ * data map in the case of a job recovering after a failed scheduler
+ * instance.
+ *
+ * @see com.fr.third.org.quartz.JobDetail#requestsRecovery()
+ */
+ String FAILED_JOB_ORIGINAL_TRIGGER_NAME = "QRTZ_FAILED_JOB_ORIG_TRIGGER_NAME";
+
+ /**
+ * A constant JobDataMap
key that can be used to retrieve the
+ * group of the original Trigger
from a recovery trigger's
+ * data map in the case of a job recovering after a failed scheduler
+ * instance.
+ *
+ * @see com.fr.third.org.quartz.JobDetail#requestsRecovery()
+ */
+ String FAILED_JOB_ORIGINAL_TRIGGER_GROUP = "QRTZ_FAILED_JOB_ORIG_TRIGGER_GROUP";
+
+ /**
+ * A constant JobDataMap
key that can be used to retrieve the
+ * scheduled fire time of the original Trigger
from a recovery
+ * trigger's data map in the case of a job recovering after a failed scheduler
+ * instance.
+ *
+ * @see com.fr.third.org.quartz.JobDetail#requestsRecovery()
+ */
+ String FAILED_JOB_ORIGINAL_TRIGGER_FIRETIME_IN_MILLISECONDS = "QRTZ_FAILED_JOB_ORIG_TRIGGER_FIRETIME_IN_MILLISECONDS_AS_STRING";
+
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Returns the name of the Scheduler
.
+ *
+ * Returns the instance Id of the Scheduler
.
+ *
+ * Returns the SchedulerContext
of the Scheduler
.
+ *
+ * Starts the Scheduler
's threads that fire {@link Trigger}s
.
+ * When a scheduler is first created it is in "stand-by" mode, and will not
+ * fire triggers. The scheduler can also be put into stand-by mode by
+ * calling the standby()
method.
+ *
+ * The misfire/recovery process will be started, if it is the initial call + * to this method on this scheduler instance. + *
+ * + * @throws SchedulerException + * ifshutdown()
has been called, or there is an
+ * error within the Scheduler
.
+ *
+ * @see #startDelayed(int)
+ * @see #standby()
+ * @see #shutdown()
+ */
+ void start() throws SchedulerException;
+
+ /**
+ * + * Calls {#start()} after the indicated number of seconds. + * (This call does not block). This can be useful within applications that + * have initializers that create the scheduler immediately, before the + * resources needed by the executing jobs have been fully initialized. + *
+ * + * @throws SchedulerException + * ifshutdown()
has been called, or there is an
+ * error within the Scheduler
.
+ *
+ * @see #start()
+ * @see #standby()
+ * @see #shutdown()
+ */
+ void startDelayed(int seconds) throws SchedulerException;
+
+ /**
+ * Whether the scheduler has been started.
+ *
+ *
+ * Note: This only reflects whether {@link #start()}
has ever
+ * been called on this Scheduler, so it will return true
even
+ * if the Scheduler
is currently in standby mode or has been
+ * since shutdown.
+ *
+ * Temporarily halts the Scheduler
's firing of {@link Trigger}s
.
+ *
+ * When start()
is called (to bring the scheduler out of
+ * stand-by mode), trigger misfire instructions will NOT be applied
+ * during the execution of the start()
method - any misfires
+ * will be detected immediately afterward (by the JobStore
's
+ * normal process).
+ *
+ * The scheduler is not destroyed, and can be re-started at any time. + *
+ * + * @see #start() + * @see #pauseAll() + */ + void standby() throws SchedulerException; + + /** + * @deprecated replaced by better-named standby() method. + * @see #standby() + */ + void pause() throws SchedulerException; + + /** + *
+ * Reports whether the Scheduler
is in stand-by mode.
+ *
+ * Halts the Scheduler
's firing of {@link Trigger}s
,
+ * and cleans up all resources associated with the Scheduler. Equivalent to
+ * shutdown(false)
.
+ *
+ * The scheduler cannot be re-started. + *
+ * + * @see #shutdown(boolean) + */ + void shutdown() throws SchedulerException; + + /** + *
+ * Halts the Scheduler
's firing of {@link Trigger}s
,
+ * and cleans up all resources associated with the Scheduler.
+ *
+ * The scheduler cannot be re-started. + *
+ * + * @param waitForJobsToComplete + * iftrue
the scheduler will not allow this method
+ * to return until all currently executing jobs have completed.
+ *
+ * @see #shutdown
+ */
+ void shutdown(boolean waitForJobsToComplete)
+ throws SchedulerException;
+
+ /**
+ *
+ * Reports whether the Scheduler
has been shutdown.
+ *
+ * Get a SchedulerMetaData
object describiing the settings
+ * and capabilities of the scheduler instance.
+ *
+ * Note that the data returned is an 'instantaneous' snap-shot, and that as + * soon as it's returned, the meta data values may be different. + *
+ */ + SchedulerMetaData getMetaData() throws SchedulerException; + + /** + *
+ * Return a list of JobExecutionContext
objects that
+ * represent all currently executing Jobs in this Scheduler instance.
+ *
+ * This method is not cluster aware. That is, it will only return Jobs + * currently executing in this Scheduler instance, not across the entire + * cluster. + *
+ * + *
+ * Note that the list returned is an 'instantaneous' snap-shot, and that as
+ * soon as it's returned, the true list of executing jobs may be different.
+ * Also please read the doc associated with JobExecutionContext
-
+ * especially if you're using RMI.
+ *
+ * Set the JobFactory
that will be responsible for producing
+ * instances of Job
classes.
+ *
+ * JobFactories may be of use to those wishing to have their application
+ * produce Job
instances via some special mechanism, such as to
+ * give the opertunity for dependency injection.
+ *
+ * Add the given {@link com.fr.third.org.quartz.JobDetail}
to the
+ * Scheduler, and associate the given {@link Trigger}
with
+ * it.
+ *
+ * If the given Trigger does not reference any Job
, then it
+ * will be set to reference the Job passed with it into this method.
+ *
+ * Schedule the given {@link com.fr.third.org.quartz.Trigger}
with the
+ * Job
identified by the Trigger
's settings.
+ *
+ * Remove the indicated {@link Trigger}
from the scheduler.
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Trigger}
with the
+ * given name, and store the new given one - which must be associated
+ * with the same job (the new trigger must have the job name & group specified)
+ * - however, the new trigger need not have the same name as the old trigger.
+ *
Trigger
to be replaced.
+ * @param groupName
+ * The group name of the Trigger
to be replaced.
+ * @param newTrigger
+ * The new Trigger
to be stored.
+ * @return null
if a Trigger
with the given
+ * name & group was not found and removed from the store, otherwise
+ * the first fire time of the newly scheduled trigger.
+ */
+ Date rescheduleJob(String triggerName,
+ String groupName, Trigger newTrigger) throws SchedulerException;
+
+
+ /**
+ *
+ * Add the given Job
to the Scheduler - with no associated
+ * Trigger
. The Job
will be 'dormant' until
+ * it is scheduled with a Trigger
, or Scheduler.triggerJob()
+ * is called for it.
+ *
+ * The Job
must by definition be 'durable', if it is not,
+ * SchedulerException will be thrown.
+ *
replace
is false
.
+ */
+ void addJob(JobDetail jobDetail, boolean replace)
+ throws SchedulerException;
+
+ /**
+ *
+ * Delete the identified Job
from the Scheduler - and any
+ * associated Trigger
s.
+ *
+ * Trigger the identified {@link com.fr.third.org.quartz.JobDetail}
+ * (execute it now) - the generated trigger will be non-volatile.
+ *
+ * Trigger the identified {@link com.fr.third.org.quartz.JobDetail}
+ * (execute it now) - the generated trigger will be volatile.
+ *
+ * Trigger the identified {@link com.fr.third.org.quartz.JobDetail}
+ * (execute it now) - the generated trigger will be non-volatile.
+ *
null
) JobDataMap to be
+ * associated with the trigger that fires the job immediately.
+ */
+ void triggerJob(String jobName, String groupName, JobDataMap data)
+ throws SchedulerException;
+
+ /**
+ *
+ * Trigger the identified {@link com.fr.third.org.quartz.JobDetail}
+ * (execute it now) - the generated trigger will be volatile.
+ *
null
) JobDataMap to be
+ * associated with the trigger that fires the job immediately.
+ */
+ void triggerJobWithVolatileTrigger(String jobName, String groupName, JobDataMap data)
+ throws SchedulerException;
+
+ /**
+ *
+ * Pause the {@link com.fr.third.org.quartz.JobDetail}
with the given
+ * name - by pausing all of its current Trigger
s.
+ *
+ * Pause all of the {@link com.fr.third.org.quartz.JobDetail}s
in the
+ * given group - by pausing all of their Trigger
s.
+ *
+ * The Scheduler will "remember" that the group is paused, and impose the + * pause on any new jobs that are added to the group while the group is + * paused. + *
+ * + * @see #resumeJobGroup(String) + */ + void pauseJobGroup(String groupName) throws SchedulerException; + + /** + *
+ * Pause the {@link Trigger}
with the given name.
+ *
+ * Pause all of the {@link Trigger}s
in the given group.
+ *
+ * The Scheduler will "remember" that the group is paused, and impose the + * pause on any new triggers that are added to the group while the group is + * paused. + *
+ * + * @see #resumeTriggerGroup(String) + */ + void pauseTriggerGroup(String groupName) throws SchedulerException; + + /** + *
+ * Resume (un-pause) the {@link com.fr.third.org.quartz.JobDetail}
with
+ * the given name.
+ *
+ * If any of the Job
'sTrigger
s missed one
+ * or more fire-times, then the Trigger
's misfire
+ * instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link com.fr.third.org.quartz.JobDetail}s
+ * in the given group.
+ *
+ * If any of the Job
s had Trigger
s that
+ * missed one or more fire-times, then the Trigger
's
+ * misfire instruction will be applied.
+ *
+ * Resume (un-pause) the {@link Trigger}
with the given
+ * name.
+ *
+ * If the Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link Trigger}s
in the
+ * given group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Pause all triggers - similar to calling pauseTriggerGroup(group)
+ * on every group, however, after using this method resumeAll()
+ * must be called to clear the scheduler's state of 'remembering' that all
+ * new triggers will be paused as they are added.
+ *
+ * When resumeAll()
is called (to un-pause), trigger misfire
+ * instructions WILL be applied.
+ *
+ * Resume (un-pause) all triggers - similar to calling
+ * resumeTriggerGroup(group)
on every group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Get the names of all known {@link com.fr.third.org.quartz.JobDetail}
+ * groups.
+ *
+ * Get the names of all the {@link com.fr.third.org.quartz.JobDetail}s
+ * in the given group.
+ *
+ * Get all {@link Trigger}
s that are associated with the
+ * identified {@link com.fr.third.org.quartz.JobDetail}
.
+ *
+ * Get the names of all known {@link Trigger}
groups.
+ *
+ * Get the names of all the {@link Trigger}s
in the given
+ * group.
+ *
+ * Get the names of all {@link Trigger}
groups that are paused.
+ *
+ * Get the {@link JobDetail}
for the Job
+ * instance with the given name and group.
+ *
+ * Get the {@link Trigger}
instance with the given name and
+ * group.
+ *
+ * Get the current state of the identified {@link Trigger}
.
+ *
+ * Add (register) the given Calendar
to the Scheduler.
+ *
replace
is
+ * false
.
+ */
+ void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers)
+ throws SchedulerException;
+
+ /**
+ *
+ * Delete the identified Calendar
from the Scheduler.
+ *
+ * Get the {@link Calendar}
instance with the given name.
+ *
+ * Get the names of all registered {@link Calendar}s
.
+ *
+ * Request the interruption, within this Scheduler instance, of all
+ * currently executing instances of the identified Job
, which
+ * must be an implementor of the InterruptableJob
interface.
+ *
+ * If more than one instance of the identified job is currently executing,
+ * the InterruptableJob#interrupt()
method will be called on
+ * each instance. However, there is a limitation that in the case that
+ * interrupt()
on one instances throws an exception, all
+ * remaining instances (that have not yet been interrupted) will not have
+ * their interrupt()
method called.
+ *
+ * If you wish to interrupt a specific instance of a job (when more than
+ * one is executing) you can do so by calling
+ * {@link #getCurrentlyExecutingJobs()}
to obtain a handle
+ * to the job instance, and then invoke interrupt()
on it
+ * yourself.
+ *
+ * This method is not cluster aware. That is, it will only interrupt + * instances of the identified InterruptableJob currently executing in this + * Scheduler instance, not across the entire cluster. + *
+ * + * @param jobName + * @param groupName + * @return true is at least one instance of the identified job was found + * and interrupted. + * @throws UnableToInterruptJobException if the job does not implement + *InterruptableJob
, or there is an exception while
+ * interrupting the job.
+ * @see InterruptableJob#interrupt()
+ * @see #getCurrentlyExecutingJobs()
+ */
+ boolean interrupt(String jobName, String groupName) throws UnableToInterruptJobException;
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///
+ /// Listener-related Methods
+ ///
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ *
+ * Add the given {@link JobListener}
to the Scheduler
's
+ * global list.
+ *
+ * Listeners in the 'global' list receive notification of execution events
+ * for ALL {@link com.fr.third.org.quartz.JobDetail}
s.
+ *
+ * Add the given {@link JobListener}
to the Scheduler
's
+ * list, of registered JobListener
s.
+ */
+ void addJobListener(JobListener jobListener)
+ throws SchedulerException;
+
+ /**
+ *
+ * Remove the given {@link JobListener}
from the Scheduler
's
+ * list of global listeners.
+ *
{@link #removeGlobalJobListener(String)}
+ */
+ boolean removeGlobalJobListener(JobListener jobListener)
+ throws SchedulerException;
+
+ /**
+ *
+ * Remove the identifed {@link JobListener}
from the Scheduler
's
+ * list of global listeners.
+ *
+ * Remove the identifed {@link JobListener}
from the Scheduler
's
+ * list of registered listeners.
+ *
+ * Get a List containing all of the {@link JobListener}
s in
+ * the Scheduler
'sglobal list.
+ *
+ * Get a Set containing the names of all the non-global{@link JobListener}
+ * s registered with the Scheduler
.
+ *
+ * Get the global{@link JobListener}
that has
+ * the given name.
+ *
+ * Get the non-global{@link JobListener}
that has
+ * the given name.
+ *
+ * Add the given {@link TriggerListener}
to the Scheduler
's
+ * global list.
+ *
+ * Listeners in the 'global' list receive notification of execution events
+ * for ALL {@link Trigger}
s.
+ *
+ * Add the given {@link TriggerListener}
to the Scheduler
's
+ * list, of registered TriggerListener
s.
+ */
+ void addTriggerListener(TriggerListener triggerListener)
+ throws SchedulerException;
+
+ /**
+ *
+ * Remove the given {@link TriggerListener}
from the Scheduler
's
+ * list of global listeners.
+ *
{@link #removeGlobalTriggerListener(String)}
+ */
+ boolean removeGlobalTriggerListener(TriggerListener triggerListener)
+ throws SchedulerException;
+
+ /**
+ *
+ * Remove the identifed {@link TriggerListener}
from the Scheduler
's
+ * list of global listeners.
+ *
+ * Remove the identifed {@link TriggerListener}
from the
+ * Scheduler
's list of registered listeners.
+ *
+ * Get a List containing all of the {@link TriggerListener}
+ * s in the Scheduler
'sglobal list.
+ *
+ * Get a Set containing the names of all the non-global{@link TriggerListener}
+ * s registered with the Scheduler
.
+ *
+ * Get the global{@link TriggerListener}
that
+ * has the given name.
+ *
+ * Get the non-global{@link TriggerListener}
that
+ * has the given name.
+ *
+ * Register the given {@link SchedulerListener}
with the
+ * Scheduler
.
+ *
+ * Remove the given {@link SchedulerListener}
from the
+ * Scheduler
.
+ *
+ * Get a List containing all of the {@link SchedulerListener}
+ * s registered with the Scheduler
.
+ *
+ * An exception that is thrown to indicate that there is a misconfiguration of
+ * the SchedulerFactory
- or one of the components it
+ * configures.
+ *
+ * Create a JobPersistenceException
with the given message.
+ *
+ * Create a JobPersistenceException
with the given message
+ * and cause.
+ *
+ * Holds context/environment data that can be made available to Jobs as they + * are executed. This feature is much like the ServletContext feature when + * working with J2EE servlets. + *
+ * + *
+ * Future versions of Quartz may make distinctions on how it propogates
+ * data in SchedulerContext
between instances of proxies to a
+ * single scheduler instance - i.e. if Quartz is being used via RMI.
+ *
SchedulerContext
.
+ */
+ public SchedulerContext() {
+ super(15);
+ }
+
+ /**
+ * Create a SchedulerContext
with the given data.
+ */
+ public SchedulerContext(Map map) {
+ this();
+
+ putAll(map);
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SchedulerException.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SchedulerException.java
new file mode 100644
index 000000000..604d1b717
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SchedulerException.java
@@ -0,0 +1,335 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+import com.fr.third.org.quartz.utils.ExceptionHelper;
+
+/**
+ *
+ * Base class for exceptions thrown by the Quartz {@link Scheduler}
.
+ *
+ * SchedulerException
s may contain a reference to another
+ * Exception
, which was the underlying cause of the SchedulerException
.
+ *
+ * Return the exception that is the underlying cause of this exception. + *
+ * + *+ * This may be used to find more detail about the cause of the error. + *
+ * + * @return the underlying exception, ornull
if there is not
+ * one.
+ */
+ public Throwable getUnderlyingException() {
+ return (ExceptionHelper.supportsNestedThrowable()) ?
+ ExceptionHelper.getCause(this) : cause;
+ }
+
+ /**
+ * + * Get the error code associated with this exception. + *
+ * + *+ * This may be used to find more detail about the cause of the error. + *
+ * + * @return one of the ERR_XXX constants defined in this class. + */ + public int getErrorCode() { + return errorCode; + } + + /** + *+ * Get the error code associated with this exception. + *
+ * + *+ * This may be used to provide more detail about the cause of the error. + *
+ * + * @param errorCode + * one of the ERR_XXX constants defined in this class. + */ + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + /** + *
+ * Determine if the specified error code is in the 'ERR_PERSISTENCE'
+ * category of errors.
+ *
+ * Determine if the specified error code is in the 'ERR_THREAD_POOL'
+ * category of errors.
+ *
+ * Determine if the specified error code is in the 'ERR_JOB_LISTENER'
+ * category of errors.
+ *
+ * Determine if the specified error code is in the 'ERR_TRIGGER_LISTENER'
+ * category of errors.
+ *
+ * Determine if the specified error code is in the 'ERR_CLIENT_ERROR'
+ * category of errors.
+ *
+ * Determine if the specified error code is in the 'ERR_CLIENT_ERROR'
+ * category of errors.
+ *
+ * Print a stack trace to the standard error stream. + *
+ * + *+ * This overridden version will print the nested stack trace if available, + * otherwise it prints only this exception's stack. + *
+ */ + public void printStackTrace() { + printStackTrace(System.err); + } + + /** + *+ * Print a stack trace to the specified stream. + *
+ * + *+ * This overridden version will print the nested stack trace if available, + * otherwise it prints only this exception's stack. + *
+ * + * @param out + * the stream to which the stack traces will be printed. + */ + public void printStackTrace(PrintStream out) { + super.printStackTrace(out); + + if (cause != null) { + synchronized (out) { + out.println("* Nested Exception (Underlying Cause) ---------------"); + cause.printStackTrace(out); + } + } + } + + /** + *+ * Print a stack trace to the specified writer. + *
+ * + *+ * This overridden version will print the nested stack trace if available, + * otherwise it prints this exception's stack. + *
+ * + * @param out + * the writer to which the stack traces will be printed. + */ + public void printStackTrace(PrintWriter out) { + super.printStackTrace(out); + + if (cause != null) { + synchronized (out) { + out.println("* Nested Exception (Underlying Cause) ---------------"); + cause.printStackTrace(out); + } + } + } + +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SchedulerFactory.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SchedulerFactory.java new file mode 100644 index 000000000..08af7a323 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SchedulerFactory.java @@ -0,0 +1,72 @@ + +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz; + +import java.util.Collection; + +/** + *
+ * Provides a mechanism for obtaining client-usable handles to Scheduler
+ * instances.
+ *
+ * Returns a client-usable handle to a Scheduler
.
+ *
Scheduler
.
+ */
+ Scheduler getScheduler() throws SchedulerException;
+
+ /**
+ * + * Returns a handle to the Scheduler with the given name, if it exists. + *
+ */ + Scheduler getScheduler(String schedName) throws SchedulerException; + + /** + *+ * Returns handles to all known Schedulers (made by any SchedulerFactory + * within this jvm.). + *
+ */ + Collection getAllSchedulers() throws SchedulerException; + +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SchedulerListener.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SchedulerListener.java new file mode 100644 index 000000000..6eced3ac7 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SchedulerListener.java @@ -0,0 +1,148 @@ + +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz; + +/** + *
+ * The interface to be implemented by classes that want to be informed of major
+ * {@link Scheduler}
events.
+ *
+ * Called by the {@link Scheduler}
when a {@link com.fr.third.org.quartz.JobDetail}
+ * is scheduled.
+ *
+ * Called by the {@link Scheduler}
when a {@link com.fr.third.org.quartz.JobDetail}
+ * is unscheduled.
+ *
+ * Called by the {@link Scheduler}
when a {@link Trigger}
+ * has reached the condition in which it will never fire again.
+ *
+ * Called by the {@link Scheduler}
when a {@link Trigger}
+ * or group of {@link Trigger}s
has been paused.
+ *
+ * If a group was paused, then the triggerName
parameter
+ * will be null.
+ *
+ * Called by the {@link Scheduler}
when a {@link Trigger}
+ * or group of {@link Trigger}s
has been un-paused.
+ *
+ * If a group was resumed, then the triggerName
parameter
+ * will be null.
+ *
+ * Called by the {@link Scheduler}
when a {@link com.fr.third.org.quartz.JobDetail}
+ * or group of {@link com.fr.third.org.quartz.JobDetail}s
has been
+ * paused.
+ *
+ * If a group was paused, then the jobName
parameter will be
+ * null. If all jobs were paused, then both parameters will be null.
+ *
+ * Called by the {@link Scheduler}
when a {@link com.fr.third.org.quartz.JobDetail}
+ * or group of {@link com.fr.third.org.quartz.JobDetail}s
has been
+ * un-paused.
+ *
+ * If a group was resumed, then the jobName
parameter will
+ * be null. If all jobs were paused, then both parameters will be null.
+ *
+ * Called by the {@link Scheduler}
when a serious error has
+ * occured within the scheduler - such as repeated failures in the JobStore
,
+ * or the inability to instantiate a Job
instance when its
+ * Trigger
has fired.
+ *
+ * The getErrorCode()
method of the given SchedulerException
+ * can be used to determine more specific information about the type of
+ * error that was encountered.
+ *
+ * Called by the {@link Scheduler}
to inform the listener
+ * that it has shutdown.
+ *
+ * Describes the settings and capabilities of a given {@link Scheduler}
+ * instance.
+ *
+ * Returns the name of the Scheduler
.
+ *
+ * Returns the instance Id of the Scheduler
.
+ *
+ * Returns the class-name of the Scheduler
instance.
+ *
+ * Returns the Date
at which the Scheduler started running.
+ *
+ * Returns the number of jobs executed since the Scheduler
+ * started..
+ *
+ * Returns whether the Scheduler
is being used remotely (via
+ * RMI).
+ *
+ * Returns whether the scheduler has been started. + *
+ * + *
+ * Note: isStarted()
may return true
even if
+ * isInStandbyMode()
returns true
.
+ *
Scheduler
is in standby mode.
+ */
+ public boolean isInStandbyMode() {
+ return isInStandbyMode;
+ }
+
+ /**
+ * Reports whether the Scheduler
is paused.
+ *
+ * @deprecated Please use {@link #isInStandbyMode()}
.
+ *
+ * @see #isInStandbyMode()
+ */
+ public boolean isPaused() {
+ return isInStandbyMode();
+ }
+
+ /**
+ *
+ * Reports whether the Scheduler
has been shutdown.
+ *
+ * Returns the class-name of the JobStore
instance that is
+ * being used by the Scheduler
.
+ *
+ * Returns whether or not the Scheduler
'sJobStore
+ * instance supports persistence.
+ *
+ * Returns the class-name of the ThreadPool
instance that is
+ * being used by the Scheduler
.
+ *
+ * Returns the number of threads currently in the Scheduler
's
+ * ThreadPool
.
+ *
+ * Returns the version of Quartz that is running. + *
+ */ + public String getVersion() { + return version; + } + + /** + *+ * Return a simple string representation of this object. + *
+ */ + public String toString() { + try { + return getSummary(); + } catch (SchedulerException se) { + return "SchedulerMetaData: undeterminable."; + } + } + + /** + *
+ * Returns a formatted (human readable) String describing all the Scheduler
's
+ * meta-data values.
+ *
+ * The format of the String looks something like this: + * + *
+ * + * + * Quartz Scheduler 'SchedulerName' with instanceId 'SchedulerInstanceId' Scheduler class: 'com.fr.third.org.quartz.impl.StdScheduler' - running locally. Running since: '11:33am on Jul 19, 2002' Not currently paused. Number of Triggers fired: '123' Using thread pool 'com.fr.third.org.quartz.simpl.SimpleThreadPool' - with '8' threads Using job-store 'com.fr.third.org.quartz.impl.JDBCJobStore' - which supports persistence. + *+ * + * + */ + public String getSummary() throws SchedulerException { + StringBuffer str = new StringBuffer("Quartz Scheduler (v"); + str.append(getVersion()); + str.append(") '"); + + str.append(getSchedulerName()); + str.append("' with instanceId '"); + str.append(getSchedulerInstanceId()); + str.append("'\n"); + + str.append(" Scheduler class: '"); + str.append(getSchedulerClass().getName()); + str.append("'"); + if (isSchedulerRemote()) { + str.append(" - access via RMI."); + } else { + str.append(" - running locally."); + } + str.append("\n"); + + if (!isShutdown()) { + if (runningSince() != null) { + str.append(" Running since: "); + str.append(runningSince()); + } else { + str.append("NOT STARTED."); + } + str.append("\n"); + + if (isInStandbyMode()) { + str.append(" Currently in standby mode."); + } else { + str.append(" Not currently in standby mode."); + } + } else { + str.append(" Scheduler has been SHUTDOWN."); + } + str.append("\n"); + + str.append(" Number of jobs executed: "); + str.append(numJobsExecuted()); + str.append("\n"); + + str.append(" Using thread pool '"); + str.append(getThreadPoolClass().getName()); + str.append("' - with "); + str.append(getThreadPoolSize()); + str.append(" threads."); + str.append("\n"); + + str.append(" Using job-store '"); + str.append(getJobStoreClass().getName()); + str.append("' - which "); + if (jobStoreSupportsPersistence()) { + str.append("supports persistence."); + } else { + str.append("does not support persistence."); + } + str.append("\n"); + + return str.toString(); + } + +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SimpleTrigger.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SimpleTrigger.java new file mode 100644 index 000000000..6daa37523 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/SimpleTrigger.java @@ -0,0 +1,955 @@ + +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz; + +import java.util.Date; + + +/** + *
+ * A concrete {@link Trigger}
that is used to fire a {@link com.fr.third.org.quartz.JobDetail}
+ * at a given moment in time, and optionally repeated at a specified interval.
+ *
+ * Instructs the {@link Scheduler}
that upon a mis-fire
+ * situation, the {@link SimpleTrigger}
wants to be fired
+ * now by Scheduler
.
+ *
+ * NOTE: This instruction should typically only be used for
+ * 'one-shot' (non-repeating) Triggers. If it is used on a trigger with a
+ * repeat count > 0 then it is equivalent to the instruction {@link #MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT}
+ *
.
+ *
+ * Instructs the {@link Scheduler}
that upon a mis-fire
+ * situation, the {@link SimpleTrigger}
wants to be
+ * re-scheduled to 'now' (even if the associated {@link Calendar}
+ * excludes 'now') with the repeat count left as-is. This does obey the
+ * Trigger
end-time however, so if 'now' is after the
+ * end-time the Trigger
will not fire again.
+ *
+ * NOTE: Use of this instruction causes the trigger to 'forget' + * the start-time and repeat-count that it was originally setup with (this + * is only an issue if you for some reason wanted to be able to tell what + * the original values were at some later time). + *
+ */ + public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT = 2; + + /** + *
+ * Instructs the {@link Scheduler}
that upon a mis-fire
+ * situation, the {@link SimpleTrigger}
wants to be
+ * re-scheduled to 'now' (even if the associated {@link Calendar}
+ * excludes 'now') with the repeat count set to what it would be, if it had
+ * not missed any firings. This does obey the Trigger
end-time
+ * however, so if 'now' is after the end-time the Trigger
will
+ * not fire again.
+ *
+ * NOTE: Use of this instruction causes the trigger to 'forget' + * the start-time and repeat-count that it was originally setup with. + * Instead, the repeat count on the trigger will be changed to whatever + * the remaining repeat count is (this is only an issue if you for some + * reason wanted to be able to tell what the original values were at some + * later time). + *
+ * + *
+ * NOTE: This instruction could cause the Trigger
+ * to go to the 'COMPLETE' state after firing 'now', if all the
+ * repeat-fire-times where missed.
+ *
+ * Instructs the {@link Scheduler}
that upon a mis-fire
+ * situation, the {@link SimpleTrigger}
wants to be
+ * re-scheduled to the next scheduled time after 'now' - taking into
+ * account any associated {@link Calendar}
, and with the
+ * repeat count set to what it would be, if it had not missed any firings.
+ *
+ * NOTE/WARNING: This instruction could cause the Trigger
+ * to go directly to the 'COMPLETE' state if all fire-times where missed.
+ *
+ * Instructs the {@link Scheduler}
that upon a mis-fire
+ * situation, the {@link SimpleTrigger}
wants to be
+ * re-scheduled to the next scheduled time after 'now' - taking into
+ * account any associated {@link Calendar}
, and with the
+ * repeat count left unchanged.
+ *
+ * NOTE/WARNING: This instruction could cause the Trigger
+ * to go directly to the 'COMPLETE' state if the end-time of the trigger
+ * has arrived.
+ *
+ * Used to indicate the 'repeat count' of the trigger is indefinite. Or in + * other words, the trigger should repeat continually until the trigger's + * ending timestamp. + *
+ */ + public static final int REPEAT_INDEFINITELY = -1; + + private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = 2299; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private Date startTime = null; + + private Date endTime = null; + + private Date nextFireTime = null; + + private Date previousFireTime = null; + + private int repeatCount = 0; + + private long repeatInterval = 0; + + private int timesTriggered = 0; + + private boolean complete = false; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *
+ * Create a SimpleTrigger
with no settings.
+ *
+ * Create a SimpleTrigger
that will occur immediately, and
+ * not repeat.
+ *
+ * Create a SimpleTrigger
that will occur immediately, and
+ * repeat at the the given interval the given number of times.
+ *
+ * Create a SimpleTrigger
that will occur at the given time,
+ * and not repeat.
+ *
+ * Create a SimpleTrigger
that will occur at the given time,
+ * and repeat at the the given interval the given number of times, or until
+ * the given end time.
+ *
Date
set to the time for the Trigger
+ * to fire.
+ * @param endTime
+ * A Date
set to the time for the Trigger
+ * to quit repeat firing.
+ * @param repeatCount
+ * The number of times for the Trigger
to repeat
+ * firing, use {@link #REPEAT_INDEFINITELY} for unlimited times.
+ * @param repeatInterval
+ * The number of milliseconds to pause between the repeat firing.
+ */
+ public SimpleTrigger(String name, String group, Date startTime,
+ Date endTime, int repeatCount, long repeatInterval) {
+ super(name, group);
+
+ setStartTime(startTime);
+ setEndTime(endTime);
+ setRepeatCount(repeatCount);
+ setRepeatInterval(repeatInterval);
+ }
+
+ /**
+ *
+ * Create a SimpleTrigger
that will occur at the given time,
+ * fire the identified Job
and repeat at the the given
+ * interval the given number of times, or until the given end time.
+ *
Date
set to the time for the Trigger
+ * to fire.
+ * @param endTime
+ * A Date
set to the time for the Trigger
+ * to quit repeat firing.
+ * @param repeatCount
+ * The number of times for the Trigger
to repeat
+ * firing, use {@link #REPEAT_INDEFINITELY}for unlimitted times.
+ * @param repeatInterval
+ * The number of milliseconds to pause between the repeat firing.
+ */
+ public SimpleTrigger(String name, String group, String jobName,
+ String jobGroup, Date startTime, Date endTime, int repeatCount,
+ long repeatInterval) {
+ super(name, group, jobName, jobGroup);
+
+ setStartTime(startTime);
+ setEndTime(endTime);
+ setRepeatCount(repeatCount);
+ setRepeatInterval(repeatInterval);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Get the time at which the SimpleTrigger
should occur.
+ *
+ * Set the time at which the SimpleTrigger
should occur.
+ *
null
.
+ */
+ public void setStartTime(Date startTime) {
+ if (startTime == null) {
+ throw new IllegalArgumentException("Start time cannot be null");
+ }
+
+ Date eTime = getEndTime();
+ if (eTime != null && startTime != null && eTime.before(startTime)) {
+ throw new IllegalArgumentException(
+ "End time cannot be before start time");
+ }
+
+ this.startTime = startTime;
+ }
+
+ /**
+ *
+ * Get the time at which the SimpleTrigger
should quit
+ * repeating - even if repeastCount isn't yet satisfied.
+ *
+ * Set the time at which the SimpleTrigger
should quit
+ * repeating (and be automatically deleted).
+ *
+ * Get the the number of times the SimpleTrigger
should
+ * repeat, after which it will be automatically deleted.
+ *
+ * Set the the number of time the SimpleTrigger
should
+ * repeat, after which it will be automatically deleted.
+ *
+ * Get the the time interval (in milliseconds) at which the SimpleTrigger
+ * should repeat.
+ *
+ * Set the the time interval (in milliseconds) at which the SimpleTrigger
+ * should repeat.
+ *
+ * Get the number of times the SimpleTrigger
has already
+ * fired.
+ *
+ * Set the number of times the SimpleTrigger
has already
+ * fired.
+ *
+ * Updates the SimpleTrigger
's state based on the
+ * MISFIRE_INSTRUCTION_XXX that was selected when the SimpleTrigger
+ * was created.
+ *
+ * If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY,
+ * then the following scheme will be used:
+ *
0
, then the instruction will
+ * be interpreted as MISFIRE_INSTRUCTION_FIRE_NOW
.REPEAT_INDEFINITELY
, then
+ * the instruction will be interpreted as MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
.
+ * WARNING: using MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT
+ * with a trigger that has a non-null end-time may cause the trigger to
+ * never fire again if the end-time arrived during the misfire time span.
+ * > 0
, then the instruction
+ * will be interpreted as MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT
.
+ *
+ * Called when the {@link Scheduler}
has decided to 'fire'
+ * the trigger (execute the associated Job
), in order to
+ * give the Trigger
a chance to update itself for its next
+ * triggering (if any).
+ *
+ * Called by the scheduler at the time a Trigger
is first
+ * added to the scheduler, in order to have the Trigger
+ * compute its first fire time, based on any associated calendar.
+ *
+ * After this method has been called, getNextFireTime()
+ * should return a valid answer.
+ *
Trigger
will be fired
+ * by the scheduler, which is also the same value getNextFireTime()
+ * will return (until after the first firing of the Trigger
).
+ *
+ */
+ public Date computeFirstFireTime(Calendar calendar) {
+ nextFireTime = getStartTime();
+
+ while (nextFireTime != null && calendar != null
+ && !calendar.isTimeIncluded(nextFireTime.getTime())) {
+ nextFireTime = getFireTimeAfter(nextFireTime);
+
+ if(nextFireTime == null)
+ break;
+
+ //avoid infinite loop
+ java.util.Calendar c = java.util.Calendar.getInstance();
+ c.setTime(nextFireTime);
+ if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
+ return null;
+ }
+ }
+
+ return nextFireTime;
+ }
+
+ /**
+ *
+ * Called after the {@link Scheduler}
has executed the
+ * {@link com.fr.third.org.quartz.JobDetail}
associated with the Trigger
+ * in order to get the final instruction code from the trigger.
+ *
JobExecutionContext
that was used by the
+ * Job
'sexecute(xx)
method.
+ * @param result
+ * is the JobExecutionException
thrown by the
+ * Job
, if any (may be null).
+ * @return one of the Trigger.INSTRUCTION_XXX constants.
+ *
+ * @see #INSTRUCTION_NOOP
+ * @see #INSTRUCTION_RE_EXECUTE_JOB
+ * @see #INSTRUCTION_DELETE_TRIGGER
+ * @see #INSTRUCTION_SET_TRIGGER_COMPLETE
+ * @see #triggered(Calendar)
+ */
+ public int executionComplete(JobExecutionContext context,
+ JobExecutionException result) {
+ if (result != null && result.refireImmediately()) {
+ return INSTRUCTION_RE_EXECUTE_JOB;
+ }
+
+ if (result != null && result.unscheduleFiringTrigger()) {
+ return INSTRUCTION_SET_TRIGGER_COMPLETE;
+ }
+
+ if (result != null && result.unscheduleAllTriggers()) {
+ return INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE;
+ }
+
+ if (!mayFireAgain()) {
+ return INSTRUCTION_DELETE_TRIGGER;
+ }
+
+ return INSTRUCTION_NOOP;
+ }
+
+ /**
+ *
+ * Returns the next time at which the Trigger
is scheduled to fire. If
+ * the trigger will not fire again, null
will be returned. Note that
+ * the time returned can possibly be in the past, if the time that was computed
+ * for the trigger to next fire has already arrived, but the scheduler has not yet
+ * been able to fire the trigger (which would likely be due to lack of resources
+ * e.g. threads).
+ *
The value returned is not guaranteed to be valid until after the Trigger
+ * has been added to the scheduler.
+ *
+ * Returns the previous time at which the SimpleTrigger
+ * fired. If the trigger has not yet fired, null
will be
+ * returned.
+ */
+ public Date getPreviousFireTime() {
+ return previousFireTime;
+ }
+
+ /**
+ *
+ * Set the next time at which the SimpleTrigger
should fire.
+ *
+ * This method should not be invoked by client code. + *
+ */ + public void setNextFireTime(Date nextFireTime) { + this.nextFireTime = nextFireTime; + } + + /** + *
+ * Set the previous time at which the SimpleTrigger
fired.
+ *
+ * This method should not be invoked by client code. + *
+ */ + public void setPreviousFireTime(Date previousFireTime) { + this.previousFireTime = previousFireTime; + } + + /** + *
+ * Returns the next time at which the SimpleTrigger
will
+ * fire, after the given time. If the trigger will not fire after the given
+ * time, null
will be returned.
+ *
+ * Returns the last time at which the SimpleTrigger
will
+ * fire, before the given time. If the trigger will not fire before the
+ * given time, null
will be returned.
+ *
+ * Returns the final time at which the SimpleTrigger
will
+ * fire, if repeatCount is REPEAT_INDEFINITELY, null will be returned.
+ *
+ * Note that the return time may be in the past. + *
+ */ + public Date getFinalFireTime() { + if (repeatCount == 0) { + return startTime; + } + + if (repeatCount == REPEAT_INDEFINITELY) { + return (getEndTime() == null) ? null : getFireTimeBefore(getEndTime()); + } + + long lastTrigger = startTime.getTime() + (repeatCount * repeatInterval); + + if ((getEndTime() == null) || (lastTrigger < getEndTime().getTime())) { + return new Date(lastTrigger); + } else { + return getFireTimeBefore(getEndTime()); + } + } + + /** + *
+ * Determines whether or not the SimpleTrigger
will occur
+ * again.
+ *
+ * Validates whether the properties of the JobDetail
are
+ * valid for submission into a Scheduler
.
+ *
+ * @throws IllegalStateException
+ * if a required property (such as Name, Group, Class) is not
+ * set.
+ */
+ public void validate() throws SchedulerException {
+ super.validate();
+
+ if (repeatCount != 0 && repeatInterval < 1) {
+ throw new SchedulerException("Repeat Interval cannot be zero.",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+ }
+
+ /**
+ * Used by extensions of SimpleTrigger to imply that there are additional
+ * properties, specifically so that extensions can choose whether to be
+ * stored as a serialized blob, or as a flattened SimpleTrigger table.
+ */
+ public boolean hasAdditionalProperties() {
+ return false;
+ }
+
+ public static void main(String[] args) // TODO: remove method after good
+ // unit testing
+ throws Exception {
+
+ Date sdt = new Date();
+
+ Date edt = new Date(sdt.getTime() + 55000L);
+
+ SimpleTrigger st = new SimpleTrigger("t", "g", "j", "g", sdt, edt, 10,
+ 10000L);
+
+ System.err.println();
+
+ st.computeFirstFireTime(null);
+
+ System.err.println("lastTime=" + st.getFinalFireTime());
+
+ java.util.List times = TriggerUtils.computeFireTimes(st, null, 50);
+
+ for (int i = 0; i < times.size(); i++) {
+ System.err.println("firetime = " + times.get(i));
+ }
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/StatefulJob.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/StatefulJob.java
new file mode 100644
index 000000000..02c483afb
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/StatefulJob.java
@@ -0,0 +1,57 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz;
+
+/**
+ *
+ * A marker interface for {@link com.fr.third.org.quartz.JobDetail}
s that
+ * wish to have their state maintained between executions.
+ *
+ * StatefulJob
instances follow slightly different rules from
+ * regular Job
instances. The key difference is that their
+ * associated {@link JobDataMap}
is re-persisted after every
+ * execution of the job, thus preserving state for the next execution. The
+ * other difference is that stateful jobs are not allowed to execute
+ * concurrently, which means new triggers that occur before the completion of
+ * the execute(xx)
method will be delayed.
+ *
+ * The base abstract class to be extended by all Trigger
s.
+ *
+ * Triggers
s have a name and group associated with them, which
+ * should uniquely identify them within a single {@link Scheduler}
.
+ *
+ * Trigger
s are the 'mechanism' by which Job
s
+ * are scheduled. Many Trigger
s can point to the same Job
,
+ * but a single Trigger
can only point to one Job
.
+ *
+ * Triggers can 'send' parameters/data to Job
s by placing contents
+ * into the JobDataMap
on the Trigger
.
+ *
+ * Instructs the {@link Scheduler}
that the {@link Trigger}
+ * has no further instructions.
+ *
+ * Instructs the {@link Scheduler}
that the {@link Trigger}
+ * wants the {@link com.fr.third.org.quartz.JobDetail}
to re-execute
+ * immediately. If not in a 'RECOVERING' or 'FAILED_OVER' situation, the
+ * execution context will be re-used (giving the Job
the
+ * abilitiy to 'see' anything placed in the context by its last execution).
+ *
+ * Instructs the {@link Scheduler}
that the {@link Trigger}
+ * should be put in the COMPLETE
state.
+ *
+ * Instructs the {@link Scheduler}
that the {@link Trigger}
+ * wants itself deleted.
+ *
+ * Instructs the {@link Scheduler}
that all Trigger
+ * s referencing the same {@link com.fr.third.org.quartz.JobDetail}
as
+ * this one should be put in the COMPLETE
state.
+ *
+ * Instructs the {@link Scheduler}
that all Trigger
+ * s referencing the same {@link com.fr.third.org.quartz.JobDetail}
as
+ * this one should be put in the ERROR
state.
+ *
+ * Instructs the {@link Scheduler}
that the Trigger
+ * should be put in the ERROR
state.
+ *
+ * Instructs the {@link Scheduler}
that upon a mis-fire
+ * situation, the updateAfterMisfire()
method will be called
+ * on the Trigger
to determine the mis-fire instruction.
+ *
+ * In order to see if this instruction fits your needs, you should look at
+ * the documentation for the getSmartMisfirePolicy()
method
+ * on the particular Trigger
implementation you are using.
+ *
+ * Indicates that the Trigger
is in the "normal" state.
+ *
+ * Indicates that the Trigger
is in the "paused" state.
+ *
+ * Indicates that the Trigger
is in the "complete" state.
+ *
+ * "Complete" indicates that the trigger has not remaining fire-times in + * its schedule. + *
+ */ + public static final int STATE_COMPLETE = 2; + + /** + *
+ * Indicates that the Trigger
is in the "error" state.
+ *
+ * A Trigger
arrives at the error state when the scheduler
+ * attempts to fire it, but cannot due to an error creating and executing
+ * its related job. Often this is due to the Job
's
+ * class not existing in the classpath.
+ *
+ * When the trigger is in the error state, the scheduler will make no + * attempts to fire it. + *
+ */ + public static final int STATE_ERROR = 3; + + + /** + *
+ * Indicates that the Trigger
is in the "blocked" state.
+ *
+ * A Trigger
arrives at the blocked state when the job that
+ * it is associated with is a StatefulJob
and it is
+ * currently executing.
+ *
+ * Indicates that the Trigger
does not exist.
+ *
+ * Create a Trigger
with no specified name, group, or {@link com.fr.third.org.quartz.JobDetail}
.
+ *
+ * Note that the {@link #setName(String)},{@link #setGroup(String)}and
+ * the {@link #setJobName(String)}and {@link #setJobGroup(String)}methods
+ * must be called before the Trigger
can be placed into a
+ * {@link Scheduler}.
+ *
+ * Create a Trigger
with the given name, and group.
+ *
+ * Note that the {@link #setJobName(String)}and
+ * {@link #setJobGroup(String)}methods must be called before the Trigger
+ * can be placed into a {@link Scheduler}.
+ *
null
, Scheduler.DEFAULT_GROUP will be used.
+ *
+ * @exception IllegalArgumentException
+ * if name is null or empty, or the group is an empty string.
+ */
+ public Trigger(String name, String group) {
+ setName(name);
+ setGroup(group);
+ }
+
+ /**
+ *
+ * Create a Trigger
with the given name, and group.
+ *
null
, Scheduler.DEFAULT_GROUP will be used.
+ *
+ * @exception IllegalArgumentException
+ * if name is null or empty, or the group is an empty string.
+ */
+ public Trigger(String name, String group, String jobName, String jobGroup) {
+ setName(name);
+ setGroup(group);
+ setJobName(jobName);
+ setJobGroup(jobGroup);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Get the name of this Trigger
.
+ *
+ * Set the name of this Trigger
.
+ *
+ * Get the group of this Trigger
.
+ *
+ * Set the name of this Trigger
.
+ *
null
, Scheduler.DEFAULT_GROUP will be used.
+ *
+ * @exception IllegalArgumentException
+ * if group is an empty string.
+ */
+ public void setGroup(String group) {
+ if (group != null && group.trim().length() == 0) {
+ throw new IllegalArgumentException(
+ "Group name cannot be an empty string.");
+ }
+
+ if(group == null) {
+ group = Scheduler.DEFAULT_GROUP;
+ }
+
+ this.group = group;
+ }
+
+ /**
+ *
+ * Get the name of the associated {@link com.fr.third.org.quartz.JobDetail}
.
+ *
+ * Set the name of the associated {@link com.fr.third.org.quartz.JobDetail}
.
+ *
+ * Get the name of the associated {@link com.fr.third.org.quartz.JobDetail}
's
+ * group.
+ *
+ * Set the name of the associated {@link com.fr.third.org.quartz.JobDetail}
's
+ * group.
+ *
null
, Scheduler.DEFAULT_GROUP will be used.
+ *
+ * @exception IllegalArgumentException
+ * if group is an empty string.
+ */
+ public void setJobGroup(String jobGroup) {
+ if (jobGroup != null && jobGroup.trim().length() == 0) {
+ throw new IllegalArgumentException(
+ "Group name cannot be null or empty.");
+ }
+
+ if(jobGroup == null) {
+ jobGroup = Scheduler.DEFAULT_GROUP;
+ }
+
+ this.jobGroup = jobGroup;
+ }
+
+ /**
+ *
+ * Returns the 'full name' of the Trigger
in the format
+ * "group.name".
+ *
+ * Returns the 'full name' of the Job
that the Trigger
+ * points to, in the format "group.name".
+ *
+ * Return the description given to the Trigger
instance by
+ * its creator (if any).
+ *
+ * Set a description for the Trigger
instance - may be
+ * useful for remembering/displaying the purpose of the trigger, though the
+ * description has no meaning to Quartz.
+ *
+ * Set whether or not the Trigger
should be persisted in the
+ * {@link com.fr.third.org.quartz.spi.JobStore}
for re-use after program
+ * restarts.
+ *
+ * Associate the {@link Calendar}
with the given name with
+ * this Trigger.
+ *
null
to dis-associate a Calendar.
+ */
+ public void setCalendarName(String calendarName) {
+ this.calendarName = calendarName;
+ }
+
+ /**
+ *
+ * Get the name of the {@link Calendar}
associated with this
+ * Trigger.
+ *
null
if there is no associated Calendar.
+ */
+ public String getCalendarName() {
+ return calendarName;
+ }
+
+ /**
+ *
+ * Get the JobDataMap
that is associated with the
+ * Trigger
.
+ *
+ * Changes made to this map during job execution are not re-persisted, and
+ * in fact typically result in an IllegalStateException
.
+ *
+ * Set the JobDataMap
to be associated with the
+ * Trigger
.
+ *
+ * Whether or not the Trigger
should be persisted in the
+ * {@link com.fr.third.org.quartz.spi.JobStore}
for re-use after program
+ * restarts.
+ *
+ * If not explicitly set, the default value is false
.
+ *
true
if the Trigger
should be
+ * garbage collected along with the {@link Scheduler}
.
+ */
+ public boolean isVolatile() {
+ return volatility;
+ }
+
+ /**
+ * The priority of a Trigger
acts as a tiebreaker such that if
+ * two Trigger
s have the same scheduled fire time, then the
+ * one with the higher priority will get first access to a worker
+ * thread.
+ *
+ *
+ * If not explicitly set, the default value is 5
.
+ *
Trigger
acts as a tie breaker such that if
+ * two Trigger
s have the same scheduled fire time, then Quartz
+ * will do its best to give the one with the higher priority first access
+ * to a worker thread.
+ *
+ *
+ * If not explicitly set, the default value is 5
.
+ *
+ * Add the specified name of a {@link TriggerListener}
to
+ * the end of the Trigger
's list of listeners.
+ *
+ * Remove the specified name of a {@link TriggerListener}
+ * from the Trigger
's list of listeners.
+ *
+ * Returns an array of String
containing the names of all
+ * {@link TriggerListener}
s assigned to the Trigger
,
+ * in the order in which they should be notified.
+ *
{@link TriggerListener}
s from the Trigger
.
+ */
+ public void clearAllTriggerListeners() {
+ triggerListeners.clear();
+ }
+
+ /**
+ * + * This method should not be used by the Quartz client. + *
+ * + *
+ * Called when the {@link Scheduler}
has decided to 'fire'
+ * the trigger (execute the associated Job
), in order to
+ * give the Trigger
a chance to update itself for its next
+ * triggering (if any).
+ *
+ * This method should not be used by the Quartz client. + *
+ * + *
+ * Called by the scheduler at the time a Trigger
is first
+ * added to the scheduler, in order to have the Trigger
+ * compute its first fire time, based on any associated calendar.
+ *
+ * After this method has been called, getNextFireTime()
+ * should return a valid answer.
+ *
Trigger
will be fired
+ * by the scheduler, which is also the same value getNextFireTime()
+ * will return (until after the first firing of the Trigger
).
+ *
+ */
+ public abstract Date computeFirstFireTime(Calendar calendar);
+
+ /**
+ * + * This method should not be used by the Quartz client. + *
+ * + *
+ * Called after the {@link Scheduler}
has executed the
+ * {@link com.fr.third.org.quartz.JobDetail}
associated with the Trigger
+ * in order to get the final instruction code from the trigger.
+ *
JobExecutionContext
that was used by the
+ * Job
'sexecute(xx)
method.
+ * @param result
+ * is the JobExecutionException
thrown by the
+ * Job
, if any (may be null).
+ * @return one of the Trigger.INSTRUCTION_XXX constants.
+ *
+ * @see #INSTRUCTION_NOOP
+ * @see #INSTRUCTION_RE_EXECUTE_JOB
+ * @see #INSTRUCTION_DELETE_TRIGGER
+ * @see #INSTRUCTION_SET_TRIGGER_COMPLETE
+ * @see #triggered(Calendar)
+ */
+ public abstract int executionComplete(JobExecutionContext context,
+ JobExecutionException result);
+
+ /**
+ *
+ * Used by the {@link Scheduler}
to determine whether or not
+ * it is possible for this Trigger
to fire again.
+ *
+ * If the returned value is false
then the Scheduler
+ * may remove the Trigger
from the {@link com.fr.third.org.quartz.spi.JobStore}
.
+ *
+ * Get the time at which the Trigger
should occur.
+ *
+ * The time at which the trigger's scheduling should start. May or may not + * be the first actual fire time of the trigger, depending upon the type of + * trigger and the settings of the other properties of the trigger. However + * the first actual first time will not be before this date. + *
+ *+ * Setting a value in the past may cause a new trigger to compute a first + * fire time that is in the past, which may cause an immediate misfire + * of the trigger. + *
+ */ + public abstract void setStartTime(Date startTime); + + public abstract void setEndTime(Date endTime); + + /** + *
+ * Get the time at which the Trigger
should quit repeating -
+ * even if an assigned 'repeatCount' isn't yet satisfied.
+ *
+ * Returns the next time at which the Trigger
is scheduled to fire. If
+ * the trigger will not fire again, null
will be returned. Note that
+ * the time returned can possibly be in the past, if the time that was computed
+ * for the trigger to next fire has already arrived, but the scheduler has not yet
+ * been able to fire the trigger (which would likely be due to lack of resources
+ * e.g. threads).
+ *
The value returned is not guaranteed to be valid until after the Trigger
+ * has been added to the scheduler.
+ *
+ * Returns the previous time at which the Trigger
fired.
+ * If the trigger has not yet fired, null
will be returned.
+ */
+ public abstract Date getPreviousFireTime();
+
+ /**
+ *
+ * Returns the next time at which the Trigger
will fire,
+ * after the given time. If the trigger will not fire after the given time,
+ * null
will be returned.
+ *
+ * Returns the last time at which the Trigger
will fire, if
+ * the Trigger will repeat indefinitely, null will be returned.
+ *
+ * Note that the return time *may* be in the past. + *
+ */ + public abstract Date getFinalFireTime(); + + /** + *
+ * Set the instruction the Scheduler
should be given for
+ * handling misfire situations for this Trigger
- the
+ * concrete Trigger
type that you are using will have
+ * defined a set of additional MISFIRE_INSTRUCTION_XXX
+ * constants that may be passed to this method.
+ *
+ * If not explicitly set, the default value is MISFIRE_INSTRUCTION_SMART_POLICY
.
+ *
+ * Get the instruction the Scheduler
should be given for
+ * handling misfire situations for this Trigger
- the
+ * concrete Trigger
type that you are using will have
+ * defined a set of additional MISFIRE_INSTRUCTION_XXX
+ * constants that may be passed to this method.
+ *
+ * If not explicitly set, the default value is MISFIRE_INSTRUCTION_SMART_POLICY
.
+ *
+ * This method should not be used by the Quartz client. + *
+ * + *+ * To be implemented by the concrete classes that extend this class. + *
+ * + *
+ * The implementation should update the Trigger
's state
+ * based on the MISFIRE_INSTRUCTION_XXX that was selected when the Trigger
+ * was created.
+ *
+ * This method should not be used by the Quartz client. + *
+ * + *+ * To be implemented by the concrete class. + *
+ * + *
+ * The implementation should update the Trigger
's state
+ * based on the given new version of the associated Calendar
+ * (the state should be updated so that it's next fire time is appropriate
+ * given the Calendar's new settings).
+ *
+ * Validates whether the properties of the JobDetail
are
+ * valid for submission into a Scheduler
.
+ *
+ * @throws IllegalStateException
+ * if a required property (such as Name, Group, Class) is not
+ * set.
+ */
+ public void validate() throws SchedulerException {
+ if (name == null) {
+ throw new SchedulerException("Trigger's name cannot be null",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+
+ if (group == null) {
+ throw new SchedulerException("Trigger's group cannot be null",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+
+ if (jobName == null) {
+ throw new SchedulerException(
+ "Trigger's related Job's name cannot be null",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+
+ if (jobGroup == null) {
+ throw new SchedulerException(
+ "Trigger's related Job's group cannot be null",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+ }
+
+ /**
+ *
+ * This method should not be used by the Quartz client. + *
+ * + *
+ * Usable by {@link com.fr.third.org.quartz.spi.JobStore}
+ * implementations, in order to facilitate 'recognizing' instances of fired
+ * Trigger
s as their jobs complete execution.
+ *
+ * This method should not be used by the Quartz client. + *
+ */ + public String getFireInstanceId() { + return fireInstanceId; + } + + /** + *+ * Return a simple string representation of this object. + *
+ */ + public String toString() { + return "Trigger '" + getFullName() + "': triggerClass: '" + + getClass().getName() + " isVolatile: " + isVolatile() + + " calendar: '" + getCalendarName() + "' misfireInstruction: " + + getMisfireInstruction() + " nextFireTime: " + getNextFireTime(); + } + + /** + *
+ * Compare the next fire time of this Trigger
to that of
+ * another.
+ *
+ * The interface to be implemented by classes that want to be informed when a
+ * {@link Trigger}
fires. In general, applications that use a
+ * Scheduler
will not have use for this mechanism.
+ *
+ * Get the name of the TriggerListener
.
+ *
+ * Called by the {@link Scheduler}
when a {@link Trigger}
+ * has fired, and it's associated {@link com.fr.third.org.quartz.JobDetail}
+ * is about to be executed.
+ *
+ * It is called before the vetoJobExecution(..)
method of this
+ * interface.
+ *
Trigger
that has fired.
+ * @param context
+ * The JobExecutionContext
that will be passed to
+ * the Job
'sexecute(xx)
method.
+ */
+ void triggerFired(Trigger trigger, JobExecutionContext context);
+
+ /**
+ *
+ * Called by the {@link Scheduler}
when a {@link Trigger}
+ * has fired, and it's associated {@link com.fr.third.org.quartz.JobDetail}
+ * is about to be executed.
+ *
+ * It is called after the triggerFired(..)
method of this
+ * interface.
+ *
Trigger
that has fired.
+ * @param context
+ * The JobExecutionContext
that will be passed to
+ * the Job
'sexecute(xx)
method.
+ */
+ boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);
+
+
+ /**
+ *
+ * Called by the {@link Scheduler}
when a {@link Trigger}
+ * has misfired.
+ *
+ * Consideration should be given to how much time is spent in this method, + * as it will affect all triggers that are misfiring. If you have lots + * of triggers misfiring at once, it could be an issue it this method + * does a lot. + *
+ * + * @param trigger + * TheTrigger
that has misfired.
+ */
+ void triggerMisfired(Trigger trigger);
+
+ /**
+ *
+ * Called by the {@link Scheduler}
when a {@link Trigger}
+ * has fired, it's associated {@link com.fr.third.org.quartz.JobDetail}
+ * has been executed, and it's triggered(xx)
method has been
+ * called.
+ *
Trigger
that was fired.
+ * @param context
+ * The JobExecutionContext
that was passed to the
+ * Job
'sexecute(xx)
method.
+ * @param triggerInstructionCode
+ * the result of the call on the Trigger
'striggered(xx)
+ * method.
+ */
+ void triggerComplete(Trigger trigger, JobExecutionContext context,
+ int triggerInstructionCode);
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/TriggerUtils.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/TriggerUtils.java
new file mode 100644
index 000000000..1430ca81a
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/TriggerUtils.java
@@ -0,0 +1,1401 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TimeZone;
+
+/**
+ *
+ * Convenience and utility methods for simplifying the construction and
+ * configuration of {@link Trigger}s
.
+ *
+ * Please submit suggestions for additional convenience methods to either the + * Quartz user forum or the developer's mail list at + * source forge. + *
+ * + * @see CronTrigger + * @see SimpleTrigger + * + * @author James House + */ +public class TriggerUtils { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public static final int SUNDAY = 1; + + public static final int MONDAY = 2; + + public static final int TUESDAY = 3; + + public static final int WEDNESDAY = 4; + + public static final int THURSDAY = 5; + + public static final int FRIDAY = 6; + + public static final int SATURDAY = 7; + + public static final int LAST_DAY_OF_MONTH = -1; + + public static final long MILLISECONDS_IN_MINUTE = 60l * 1000l; + + public static final long MILLISECONDS_IN_HOUR = 60l * 60l * 1000l; + + public static final long SECONDS_IN_DAY = 24l * 60l * 60L; + + public static final long MILLISECONDS_IN_DAY = SECONDS_IN_DAY * 1000l; + + /** + * Private constructor because this is a pure utility class. + */ + private TriggerUtils() { + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private static void validateDayOfWeek(int dayOfWeek) { + if (dayOfWeek < SUNDAY || dayOfWeek > SATURDAY) { + throw new IllegalArgumentException("Invalid day of week."); + } + } + + private static void validateHour(int hour) { + if (hour < 0 || hour > 23) { + throw new IllegalArgumentException( + "Invalid hour (must be >= 0 and <= 23)."); + } + } + + private static void validateMinute(int minute) { + if (minute < 0 || minute > 59) { + throw new IllegalArgumentException( + "Invalid minute (must be >= 0 and <= 59)."); + } + } + + private static void validateSecond(int second) { + if (second < 0 || second > 59) { + throw new IllegalArgumentException( + "Invalid second (must be >= 0 and <= 59)."); + } + } + + private static void validateDayOfMonth(int day) { + if ((day < 1 || day > 31) && day != LAST_DAY_OF_MONTH) { + throw new IllegalArgumentException("Invalid day of month."); + } + } + + private static void validateMonth(int month) { + if (month < 1 || month > 12) { + throw new IllegalArgumentException( + "Invalid month (must be >= 1 and <= 12."); + } + } + + private static void validateYear(int year) { + if (year < 1970 || year > 2099) { + throw new IllegalArgumentException( + "Invalid year (must be >= 1970 and <= 2099."); + } + } + + /** + *
+ * Set the given Trigger
's name to the given value, and its
+ * group to the default group (Scheduler.DEFAULT_GROUP
).
+ *
+ * Set the given Trigger
's name to the given value, and its
+ * group to the given group.
+ *
+ * Make a trigger that will fire every day at the given time. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param hour the hour (0-23) upon which to fire + * @param minute the minute (0-59) upon which to fire + * @return the new trigger + */ + public static Trigger makeDailyTrigger(int hour, int minute) { + validateHour(hour); + validateMinute(minute); + + CronTrigger trig = new CronTrigger(); + + try { + trig.setCronExpression("0 " + minute + " " + hour + " ? * *"); + } catch (Exception ignore) { + return null; /* never happens... */ + } + + trig.setStartTime(new Date()); + + return trig; + } + + /** + *+ * Make a trigger that will fire every day at the given time. + *
+ * + *+ * The generated trigger will not have its group or end-time set. + * The Start time defaults to 'now'. + *
+ * + * @param trigName the trigger's name + * @param hour the hour (0-23) upon which to fire + * @param minute the minute (0-59) upon which to fire + * @return the newly created trigger + */ + public static Trigger makeDailyTrigger( + String trigName, int hour, int minute) { + Trigger trig = makeDailyTrigger(hour, minute); + trig.setName(trigName); + return trig; + } + + /** + *+ * Make a trigger that will fire every week at the given day and time. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param dayOfWeek (1-7) the day of week upon which to fire + * @param hour the hour (0-23) upon which to fire + * @param minute the minute (0-59) upon which to fire + * @return the new trigger + * + * @see #SUNDAY + * @see #MONDAY + * @see #TUESDAY + * @see #WEDNESDAY + * @see #THURSDAY + * @see #FRIDAY + * @see #SATURDAY + */ + public static Trigger makeWeeklyTrigger( + int dayOfWeek, int hour, int minute) { + validateDayOfWeek(dayOfWeek); + validateHour(hour); + validateMinute(minute); + + CronTrigger trig = new CronTrigger(); + + try { + trig.setCronExpression("0 " + minute + " " + hour + " ? * " + + dayOfWeek); + } catch (Exception ignore) { + return null; /* never happens... */ + } + + trig.setStartTime(new Date()); + + return trig; + } + + /** + *+ * Make a trigger that will fire every week at the given day and time. + *
+ * + *+ * The generated trigger will not have its group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param trigName the trigger's name + * @param dayOfWeek (1-7) the day of week upon which to fire + * @param hour the hour (0-23) upon which to fire + * @param minute the minute (0-59) upon which to fire + * @return the newly created trigger + * + * @see #SUNDAY + * @see #MONDAY + * @see #TUESDAY + * @see #WEDNESDAY + * @see #THURSDAY + * @see #FRIDAY + * @see #SATURDAY + */ + public static Trigger makeWeeklyTrigger( + String trigName, int dayOfWeek, int hour, int minute) { + Trigger trig = makeWeeklyTrigger(dayOfWeek, hour, minute); + trig.setName(trigName); + return trig; + } + + + /** + *+ * Make a trigger that will fire every month at the given day and time. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + *+ * If the day of the month specified does not occur in a given month, a + * firing will not occur that month. (i.e. if dayOfMonth is specified as + * 31, no firing will occur in the months of the year with fewer than 31 + * days). + *
+ * + * @param dayOfMonth (1-31, or -1) the day of week upon which to fire + * @param hour the hour (0-23) upon which to fire + * @param minute the minute (0-59) upon which to fire + * @return the newly created trigger + */ + public static Trigger makeMonthlyTrigger( + int dayOfMonth, int hour, int minute) { + validateDayOfMonth(dayOfMonth); + validateHour(hour); + validateMinute(minute); + + CronTrigger trig = new CronTrigger(); + + try { + if (dayOfMonth != LAST_DAY_OF_MONTH) { + trig.setCronExpression("0 " + minute + " " + hour + " " + dayOfMonth + " * ?"); + } else { + trig.setCronExpression("0 " + minute + " " + hour + " L * ?"); + } + } catch (Exception ignore) { + return null; /* never happens... */ + } + + trig.setStartTime(new Date()); + + return trig; + } + + /** + *+ * Make a trigger that will fire every month at the given day and time. + *
+ * + *+ * The generated trigger will not have its group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + *+ * If the day of the month specified does not occur in a given month, a + * firing will not occur that month. (i.e. if dayOfMonth is specified as + * 31, no firing will occur in the months of the year with fewer than 31 + * days). + *
+ * + * @param trigName the trigger's name + * @param dayOfMonth (1-31, or -1) the day of week upon which to fire + * @param hour the hour (0-23) upon which to fire + * @param minute the minute (0-59) upon which to fire + * @return the newly created trigger + */ + public static Trigger makeMonthlyTrigger( + String trigName, int dayOfMonth, int hour, int minute) { + Trigger trig = makeMonthlyTrigger(dayOfMonth, hour, minute); + trig.setName(trigName); + return trig; + } + + /* + *Make a trigger that will fire every N days at the given time.
+ * + *TThe generated trigger will not have its name, group, + * start-time and end-time set.
+ * + * @param hour the hour (0-23) upon which to fire @param minute the minute + * (0-59) upon which to fire @param interval the number of days between + * firings public static Trigger makeDailyTrigger(int interval, int hour, + * int minute) { + * + * SimpleTrigger trig = new SimpleTrigger(); + * + * MILLISECONDS_IN_DAY); + * trig.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); + * + * return trig; + * } + */ + + /** + *
+ * Make a trigger that will fire repeatCount
times, waiting
+ * repeatInterval
milliseconds between each fire.
+ *
+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param repeatCount the number of times to fire the trigger + * @param repeatInterval the number of milliseconds to wait between fires + * @return the newly created trigger + */ + public static Trigger makeImmediateTrigger( + int repeatCount, long repeatInterval) { + SimpleTrigger trig = new SimpleTrigger(); + trig.setStartTime( new Date() ); + trig.setRepeatCount(repeatCount); + trig.setRepeatInterval(repeatInterval); + return trig; + } + + /** + *
+ * Make a trigger that will fire repeatCount
times, waiting
+ * repeatInterval
milliseconds between each fire.
+ *
+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param trigName the trigger's name + * @param repeatCount the number of times to fire the trigger + * @param repeatInterval the number of milliseconds to wait between fires + * @return the new trigger + */ + public static Trigger makeImmediateTrigger( + String trigName, int repeatCount, long repeatInterval) { + Trigger trig = makeImmediateTrigger(repeatCount, repeatInterval); + trig.setName(trigName); + return trig; + } + + /** + *+ * Make a trigger that will fire every second, indefinitely. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * @return the new trigger + */ + public static Trigger makeSecondlyTrigger() { + return makeSecondlyTrigger(1, SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + *+ * Make a trigger that will fire every second, indefinitely. + *
+ * + *+ * The generated trigger will not have its group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param trigName the trigger's name + * @return the new trigger + */ + public static Trigger makeSecondlyTrigger(String trigName) { + return makeSecondlyTrigger( + trigName, 1, SimpleTrigger.REPEAT_INDEFINITELY); + } + + + /** + *+ * Make a trigger that will fire every N seconds, indefinitely. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param intervalInSeconds the number of seconds between firings + * @return the new trigger + */ + public static Trigger makeSecondlyTrigger(int intervalInSeconds) { + return makeSecondlyTrigger( + intervalInSeconds, SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + *+ * Make a trigger that will fire every N seconds, with the given number of + * repeats. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param intervalInSeconds the number of seconds between firings + * @param repeatCount the number of times to repeat the firing + * @return the new trigger + */ + public static Trigger makeSecondlyTrigger( + int intervalInSeconds, int repeatCount) { + SimpleTrigger trig = new SimpleTrigger(); + + trig.setRepeatInterval(intervalInSeconds * 1000l); + trig.setRepeatCount(repeatCount); + trig.setStartTime(new Date()); + + return trig; + } + + /** + *+ * Make a trigger that will fire every N seconds, with the given number of + * repeats. + *
+ * + *+ * The generated trigger will not have its group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param trigName the trigger's name + * @param intervalInSeconds the number of seconds between firings + * @param repeatCount the number of times to repeat the firing + * @return the new trigger + */ + public static Trigger makeSecondlyTrigger( + String trigName, int intervalInSeconds, int repeatCount) { + Trigger trig = makeSecondlyTrigger(intervalInSeconds, repeatCount); + trig.setName(trigName); + return trig; + } + + /** + *+ * Make a trigger that will fire every minute, indefinitely. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @return the new trigger + */ + public static Trigger makeMinutelyTrigger() { + return makeMinutelyTrigger(1, SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + *+ * Make a trigger that will fire every minute, indefinitely. + *
+ * + *+ * The generated trigger will not have its group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param trigName the trigger's name + * @return the new trigger + */ + public static Trigger makeMinutelyTrigger(String trigName) { + return makeMinutelyTrigger( + trigName, 1, SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + *+ * Make a trigger that will fire every N minutes, indefinitely. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param intervalInMinutes the number of minutes between firings + * @return the new trigger + */ + public static Trigger makeMinutelyTrigger(int intervalInMinutes) { + return makeMinutelyTrigger( + intervalInMinutes, SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + *+ * Make a trigger that will fire every N minutes, with the given number of + * repeats. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param intervalInMinutes the number of minutes between firings + * @param repeatCount the number of times to repeat the firing + * @return the new trigger + */ + public static Trigger makeMinutelyTrigger( + int intervalInMinutes, int repeatCount) { + SimpleTrigger trig = new SimpleTrigger(); + + trig.setRepeatInterval(intervalInMinutes * MILLISECONDS_IN_MINUTE); + trig.setRepeatCount(repeatCount); + + trig.setStartTime(new Date()); + + return trig; + } + + /** + *+ * Make a trigger that will fire every N minutes, with the given number of + * repeats. + *
+ * + *+ * The generated trigger will not have its group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param trigName the trigger's name + * @param intervalInMinutes the number of minutes between firings + * @param repeatCount the number of times to repeat the firing + * @return the new trigger + */ + public static Trigger makeMinutelyTrigger( + String trigName, int intervalInMinutes, int repeatCount) { + Trigger trig = makeMinutelyTrigger(intervalInMinutes, repeatCount); + trig.setName(trigName); + return trig; + } + + /** + *+ * Make a trigger that will fire every hour, indefinitely. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @return the new trigger + */ + public static Trigger makeHourlyTrigger() { + return makeHourlyTrigger(1, SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + *+ * Make a trigger that will fire every hour, indefinitely. + *
+ * + *+ * The generated trigger will not have its group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param trigName the trigger's name + * @return the new trigger + */ + public static Trigger makeHourlyTrigger(String trigName) { + return makeHourlyTrigger( + trigName, 1, SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + *+ * Make a trigger that will fire every N hours, indefinitely. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param intervalInHours the number of hours between firings + * @return the new trigger + */ + public static Trigger makeHourlyTrigger(int intervalInHours) { + return makeHourlyTrigger( + intervalInHours, SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + *+ * Make a trigger that will fire every N hours, with the given number of + * repeats. + *
+ * + *+ * The generated trigger will not have its name, group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param intervalInHours the number of hours between firings + * @param repeatCount the number of times to repeat the firing + * @return the new trigger + */ + public static Trigger makeHourlyTrigger( + int intervalInHours, int repeatCount) { + SimpleTrigger trig = new SimpleTrigger(); + + trig.setRepeatInterval(intervalInHours * MILLISECONDS_IN_HOUR); + trig.setRepeatCount(repeatCount); + + trig.setStartTime(new Date()); + + return trig; + } + + /** + *+ * Make a trigger that will fire every N hours, with the given number of + * repeats. + *
+ * + *+ * The generated trigger will not have its group, + * or end-time set. The Start time defaults to 'now'. + *
+ * + * @param trigName the trigger's name + * @param intervalInHours the number of hours between firings + * @param repeatCount the number of times to repeat the firing + * @return the new trigger + */ + public static Trigger makeHourlyTrigger( + String trigName, int intervalInHours, int repeatCount) { + Trigger trig =makeHourlyTrigger(intervalInHours, repeatCount); + trig.setName(trigName); + return trig; + } + + /** + *+ * Returns a date that is rounded to the next even hour above the given + * date. + *
+ * + *+ * For example an input date with a time of 08:13:54 would result in a date + * with the time of 09:00:00. If the date's time is in the 23rd hour, the + * date's 'day' will be promoted, and the time will be set to 00:00:00. + *
+ * + * @param date + * the Date to round, ifnull
the current time will
+ * be used
+ * @return the new rounded date
+ */
+ public static Date getEvenHourDate(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) + 1);
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ * + * Returns a date that is rounded to the previous even hour below the given + * date. + *
+ * + *+ * For example an input date with a time of 08:13:54 would result in a date + * with the time of 08:00:00. + *
+ * + * @param date + * the Date to round, ifnull
the current time will
+ * be used
+ * @return the new rounded date
+ */
+ public static Date getEvenHourDateBefore(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ * + * Returns a date that is rounded to the next even minute above the given + * date. + *
+ * + *+ * For example an input date with a time of 08:13:54 would result in a date + * with the time of 08:14:00. If the date's time is in the 59th minute, + * then the hour (and possibly the day) will be promoted. + *
+ * + * @param date + * the Date to round, ifnull
the current time will
+ * be used
+ * @return the new rounded date
+ */
+ public static Date getEvenMinuteDate(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + 1);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ * + * Returns a date that is rounded to the previous even minute below the + * given date. + *
+ * + *+ * For example an input date with a time of 08:13:54 would result in a date + * with the time of 08:13:00. + *
+ * + * @param date + * the Date to round, ifnull
the current time will
+ * be used
+ * @return the new rounded date
+ */
+ public static Date getEvenMinuteDateBefore(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ * + * Returns a date that is rounded to the next even second above the given + * date. + *
+ * + * @param date + * the Date to round, ifnull
the current time will
+ * be used
+ * @return the new rounded date
+ */
+ public static Date getEvenSecondDate(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ c.set(Calendar.SECOND, c.get(Calendar.SECOND) + 1);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ * + * Returns a date that is rounded to the previous even second below the + * given date. + *
+ * + *+ * For example an input date with a time of 08:13:54.341 would result in a + * date with the time of 08:13:00.000. + *
+ * + * @param date + * the Date to round, ifnull
the current time will
+ * be used
+ * @return the new rounded date
+ */
+ public static Date getEvenSecondDateBefore(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ * + * Returns a date that is rounded to the next even multiple of the given + * minute. + *
+ * + *+ * For example an input date with a time of 08:13:54, and an input + * minute-base of 5 would result in a date with the time of 08:15:00. The + * same input date with an input minute-base of 10 would result in a date + * with the time of 08:20:00. But a date with the time 08:53:31 and an + * input minute-base of 45 would result in 09:00:00, because the even-hour + * is the next 'base' for 45-minute intervals. + *
+ * + *+ * More examples:
Input Time | + *Minute-Base | + *Result Time | + *
---|---|---|
11:16:41 | + *20 | + *11:20:00 | + *
11:36:41 | + *20 | + *11:40:00 | + *
11:46:41 | + *20 | + *12:00:00 | + *
11:26:41 | + *30 | + *11:30:00 | + *
11:36:41 | + *30 | + *12:00:00 | + *11:16:41 | + *17 | + *11:17:00 | + * + * + *11:17:41 | + *17 | + *11:34:00 | + * + * + *11:52:41 | + *17 | + *12:00:00 | + * + * + *11:52:41 | + *5 | + *11:55:00 | + * + * + *11:57:41 | + *5 | + *12:00:00 | + * + * + *11:17:41 | + *0 | + *12:00:00 | + * + * + *11:17:41 | + *1 | + *11:08:00 | + * + *
null
the current time will
+ * be used
+ * @param minuteBase
+ * the base-minute to set the time on
+ * @return the new rounded date
+ *
+ * @see #getNextGivenSecondDate(Date, int)
+ */
+ public static Date getNextGivenMinuteDate(Date date, int minuteBase) {
+ if (minuteBase < 0 || minuteBase > 59) {
+ throw new IllegalArgumentException(
+ "minuteBase must be >=0 and <= 59");
+ }
+
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ if (minuteBase == 0) {
+ c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) + 1);
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ int minute = c.get(Calendar.MINUTE);
+
+ int arItr = minute / minuteBase;
+
+ int nextMinuteOccurance = minuteBase * (arItr + 1);
+
+ if (nextMinuteOccurance < 60) {
+ c.set(Calendar.MINUTE, nextMinuteOccurance);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ } else {
+ c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) + 1);
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+ }
+
+ /**
+ * + * Returns a date that is rounded to the next even multiple of the given + * minute. + *
+ * + *
+ * The rules for calculating the second are the same as those for
+ * calculating the minute in the method
+ * getNextGivenMinuteDate(..)
.
+ *
null
the current time will
+ * be used
+ * @param secondBase the base-second to set the time on
+ * @return the new rounded date
+ *
+ * @see #getNextGivenMinuteDate(Date, int)
+ */
+ public static Date getNextGivenSecondDate(Date date, int secondBase) {
+ if (secondBase < 0 || secondBase > 59) {
+ throw new IllegalArgumentException(
+ "secondBase must be >=0 and <= 59");
+ }
+
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ if (secondBase == 0) {
+ c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + 1);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ int second = c.get(Calendar.SECOND);
+
+ int arItr = second / secondBase;
+
+ int nextSecondOccurance = secondBase * (arItr + 1);
+
+ if (nextSecondOccurance < 60) {
+ c.set(Calendar.SECOND, nextSecondOccurance);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ } else {
+ c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + 1);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+ }
+
+ /**
+ *
+ * Get a Date
object that represents the given time, on
+ * today's date.
+ *
+ * Get a Date
object that represents the given time, on the
+ * given date.
+ *
+ * Get a Date
object that represents the given time, on the
+ * given date.
+ *
Trigger
.
+ * The input trigger will be cloned before any work is done, so you need
+ * not worry about its state being altered by this method.
+ *
+ * @param trigg
+ * The trigger upon which to do the work
+ * @param cal
+ * The calendar to apply to the trigger's schedule
+ * @param numTimes
+ * The number of next fire times to produce
+ * @return List of java.util.Date objects
+ */
+ public static List computeFireTimes(Trigger trigg, com.fr.third.org.quartz.Calendar cal,
+ int numTimes) {
+ LinkedList lst = new LinkedList();
+
+ Trigger t = (Trigger) trigg.clone();
+
+ if (t.getNextFireTime() == null) {
+ t.computeFirstFireTime(cal);
+ }
+
+ for (int i = 0; i < numTimes; i++) {
+ Date d = t.getNextFireTime();
+ if (d != null) {
+ lst.add(d);
+ t.triggered(cal);
+ } else {
+ break;
+ }
+ }
+
+ return java.util.Collections.unmodifiableList(lst);
+ }
+
+ /**
+ * Returns a list of Dates that are the next fire times of a
+ * Trigger
+ * that fall within the given date range. The input trigger will be cloned
+ * before any work is done, so you need not worry about its state being
+ * altered by this method.
+ *
+ * + * NOTE: if this is a trigger that has previously fired within the given + * date range, then firings which have already occured will not be listed + * in the output List. + *
+ * + * @param trigg + * The trigger upon which to do the work + * @param cal + * The calendar to apply to the trigger's schedule + * @param from + * The starting date at which to find fire times + * @param to + * The ending date at which to stop finding fire times + * @return List of java.util.Date objects + */ + public static List computeFireTimesBetween(Trigger trigg, + com.fr.third.org.quartz.Calendar cal, Date from, Date to) { + LinkedList lst = new LinkedList(); + + Trigger t = (Trigger) trigg.clone(); + + if (t.getNextFireTime() == null) { + t.setStartTime(from); + t.setEndTime(to); + t.computeFirstFireTime(cal); + } + + // TODO: this method could be more efficient by using logic specific + // to the type of trigger ... + while (true) { + Date d = t.getNextFireTime(); + if (d != null) { + if (d.before(from)) { + t.triggered(cal); + continue; + } + if (d.after(to)) { + break; + } + lst.add(d); + t.triggered(cal); + } else { + break; + } + } + + return java.util.Collections.unmodifiableList(lst); + } + + /** + * Translate a date & time from a users timezone to the another + * (probably server) timezone to assist in creating a simple trigger with + * the right date & time. + * + * @param date the date to translate + * @param src the original time-zone + * @param dest the destination time-zone + * @return the translated date + */ + public static Date translateTime(Date date, TimeZone src, TimeZone dest) { + + Date newDate = new Date(); + + int offset = ( + getOffset(date.getTime(), dest) - getOffset(date.getTime(), src)); + + newDate.setTime(date.getTime() - offset); + + return newDate; + } + + /** + * Gets the offset from UT for the given date in the given timezone, + * taking into account daylight savings. + * + *+ * Equivalent of TimeZone.getOffset(date) in JDK 1.4, but Quartz is trying + * to support JDK 1.3. + *
+ * + * @param date the date (in milliseconds) that is the base for the offset + * @param tz the time-zone to calculate to offset to + * @return the offset + */ + public static int getOffset(long date, TimeZone tz) { + + if (tz.inDaylightTime(new Date(date))) { + return tz.getRawOffset() + getDSTSavings(tz); + } + + return tz.getRawOffset(); + } + + /** + *+ * Equivalent of TimeZone.getDSTSavings() in JDK 1.4, but Quartz is trying + * to support JDK 1.3. + *
+ * + * @param tz the target time-zone + * @return the amount of saving time in milliseconds + */ + public static int getDSTSavings(TimeZone tz) { + + if (tz.useDaylightTime()) { + return 3600000; + } + return 0; + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/UnableToInterruptJobException.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/UnableToInterruptJobException.java new file mode 100644 index 000000000..d9e0440c3 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/UnableToInterruptJobException.java @@ -0,0 +1,63 @@ + +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ + +package com.fr.third.org.quartz; + +/** + *+ * An exception that is thrown to indicate that a call to + * InterruptableJob.interrupt() failed without interrupting the Job. + *
+ * + * @see com.fr.third.org.quartz.InterruptableJob#interrupt() + * + * @author James House + */ +public class UnableToInterruptJobException extends SchedulerException { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *
+ * Create a UnableToInterruptJobException
with the given message.
+ *
+ * Create a UnableToInterruptJobException
with the given cause.
+ *
Collection
to provide additional behaviour.
+ *
+ * Each method call made on this Collection
is forwarded to the
+ * decorated Collection
. This class is used as a framework on which
+ * to build to extensions such as synchronized and unmodifiable behaviour. The
+ * main advantage of decoration is that one decorator can wrap any implementation
+ * of Collection
, whereas sub-classing requires a new class to be
+ * written for each implementation.
+ *
+ * This implementation does not perform any special processing with + * {@link #iterator()}. Instead it simply returns the value from the + * wrapped collection. This may be undesirable, for example if you are trying + * to write an unmodifiable implementation it might provide a loophole. + * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Stephen Colebourne + * @author Paul Jack + */ +public abstract class AbstractCollectionDecorator implements Collection { + + /** The collection being decorated */ + protected Collection collection; + + /** + * Constructor only used in deserialization, do not use otherwise. + * @since Commons Collections 3.1 + */ + protected AbstractCollectionDecorator() { + super(); + } + + /** + * Constructor that wraps (not copies). + * + * @param coll the collection to decorate, must not be null + * @throws IllegalArgumentException if the collection is null + */ + protected AbstractCollectionDecorator(Collection coll) { + if (coll == null) { + throw new IllegalArgumentException("Collection must not be null"); + } + this.collection = coll; + } + + /** + * Gets the collection being decorated. + * + * @return the decorated collection + */ + protected Collection getCollection() { + return collection; + } + + //----------------------------------------------------------------------- + public boolean add(Object object) { + return collection.add(object); + } + + public boolean addAll(Collection coll) { + return collection.addAll(coll); + } + + public void clear() { + collection.clear(); + } + + public boolean contains(Object object) { + return collection.contains(object); + } + + public boolean isEmpty() { + return collection.isEmpty(); + } + + public Iterator iterator() { + return collection.iterator(); + } + + public boolean remove(Object object) { + return collection.remove(object); + } + + public int size() { + return collection.size(); + } + + public Object[] toArray() { + return collection.toArray(); + } + + public Object[] toArray(Object[] object) { + return collection.toArray(object); + } + + public boolean containsAll(Collection coll) { + return collection.containsAll(coll); + } + + public boolean removeAll(Collection coll) { + return collection.removeAll(coll); + } + + public boolean retainAll(Collection coll) { + return collection.retainAll(coll); + } + + public boolean equals(Object object) { + if (object == this) { + return true; + } + return collection.equals(object); + } + + public int hashCode() { + return collection.hashCode(); + } + + public String toString() { + return collection.toString(); + } + +} + diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/AbstractIteratorDecorator.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/AbstractIteratorDecorator.java new file mode 100644 index 000000000..f6c9660a5 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/AbstractIteratorDecorator.java @@ -0,0 +1,58 @@ +package com.fr.third.org.quartz.collections; + +import java.util.Iterator; + +/** + * Provides basic behaviour for decorating an iterator with extra functionality. + *
+ * All methods are forwarded to the decorated iterator.
+ *
+ * @since Commons Collections 3.0
+ * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
+ *
+ * @author James Strachan
+ * @author Stephen Colebourne
+ */
+public class AbstractIteratorDecorator implements Iterator {
+
+ /** The iterator being decorated */
+ protected final Iterator iterator;
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor that decorates the specified iterator.
+ *
+ * @param iterator the iterator to decorate, must not be null
+ * @throws IllegalArgumentException if the collection is null
+ */
+ public AbstractIteratorDecorator(Iterator iterator) {
+ super();
+ if (iterator == null) {
+ throw new IllegalArgumentException("Iterator must not be null");
+ }
+ this.iterator = iterator;
+ }
+
+ /**
+ * Gets the iterator being decorated.
+ *
+ * @return the decorated iterator
+ */
+ protected Iterator getIterator() {
+ return iterator;
+ }
+
+ //-----------------------------------------------------------------------
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ public Object next() {
+ return iterator.next();
+ }
+
+ public void remove() {
+ iterator.remove();
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/AbstractSerializableSetDecorator.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/AbstractSerializableSetDecorator.java
new file mode 100644
index 000000000..5aa737162
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/AbstractSerializableSetDecorator.java
@@ -0,0 +1,55 @@
+package com.fr.third.org.quartz.collections;
+
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Serializable subclass of AbstractSetDecorator.
+ *
+ * @author Stephen Colebourne
+ * @since Commons Collections 3.1
+ */
+public abstract class AbstractSerializableSetDecorator
+ extends AbstractSetDecorator
+ implements Serializable {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 1229469966212206107L;
+
+ /**
+ * Constructor.
+ */
+ protected AbstractSerializableSetDecorator(Set set) {
+ super(set);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Write the set out using a custom routine.
+ *
+ * @param out the output stream
+ * @throws IOException
+ */
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeObject(collection);
+ }
+
+ /**
+ * Read the set in using a custom routine.
+ *
+ * @param in the input stream
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ collection = (Collection) in.readObject();
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/AbstractSetDecorator.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/AbstractSetDecorator.java
new file mode 100644
index 000000000..18a610c33
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/AbstractSetDecorator.java
@@ -0,0 +1,44 @@
+package com.fr.third.org.quartz.collections;
+
+import java.util.Set;
+
+/**
+ * Decorates another Set
to provide additional behaviour.
+ *
+ * Methods are forwarded directly to the decorated set.
+ *
+ * @since Commons Collections 3.0
+ * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
+ *
+ * @author Stephen Colebourne
+ */
+public abstract class AbstractSetDecorator extends AbstractCollectionDecorator implements Set {
+
+ /**
+ * Constructor only used in deserialization, do not use otherwise.
+ * @since Commons Collections 3.1
+ */
+ protected AbstractSetDecorator() {
+ super();
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param set the set to decorate, must not be null
+ * @throws IllegalArgumentException if set is null
+ */
+ protected AbstractSetDecorator(Set set) {
+ super(set);
+ }
+
+ /**
+ * Gets the set being decorated.
+ *
+ * @return the decorated set
+ */
+ protected Set getSet() {
+ return (Set) getCollection();
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/ListOrderedSet.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/ListOrderedSet.java
new file mode 100644
index 000000000..62af6910a
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/collections/ListOrderedSet.java
@@ -0,0 +1,296 @@
+package com.fr.third.org.quartz.collections;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import com.fr.third.org.quartz.collections.AbstractIteratorDecorator;
+
+/**
+ * Decorates another Set
to ensure that the order of addition
+ * is retained and used by the iterator.
+ *
+ * If an object is added to the set for a second time, it will remain in the + * original position in the iteration. + * The order can be observed from the set via the iterator or toArray methods. + *
+ * The ListOrderedSet also has various useful direct methods. These include many
+ * from List
, such as get(int)
, remove(int)
+ * and indexOf(int)
. An unmodifiable List
view of
+ * the set can be obtained via asList()
.
+ *
+ * This class cannot implement the List
interface directly as
+ * various interface methods (notably equals/hashCode) are incompatable with a set.
+ *
+ * This class is Serializable from Commons Collections 3.1. + * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Stephen Colebourne + * @author Henning P. Schmiedehausen + */ +public class ListOrderedSet extends AbstractSerializableSetDecorator implements Set { + + /** Serialization version */ + private static final long serialVersionUID = -228664372470420141L; + + /** Internal list to hold the sequence of objects */ + protected final List setOrder; + + /** + * Factory method to create an ordered set specifying the list and set to use. + *
+ * The list and set must both be empty. + * + * @param set the set to decorate, must be empty and not null + * @param list the list to decorate, must be empty and not null + * @throws IllegalArgumentException if set or list is null + * @throws IllegalArgumentException if either the set or list is not empty + * @since Commons Collections 3.1 + */ + public static ListOrderedSet decorate(Set set, List list) { + if (set == null) { + throw new IllegalArgumentException("Set must not be null"); + } + if (list == null) { + throw new IllegalArgumentException("List must not be null"); + } + if (set.size() > 0 || list.size() > 0) { + throw new IllegalArgumentException("Set and List must be empty"); + } + return new ListOrderedSet(set, list); + } + + /** + * Factory method to create an ordered set. + *
+ * An ArrayList
is used to retain order.
+ *
+ * @param set the set to decorate, must not be null
+ * @throws IllegalArgumentException if set is null
+ */
+ public static ListOrderedSet decorate(Set set) {
+ return new ListOrderedSet(set);
+ }
+
+ /**
+ * Factory method to create an ordered set using the supplied list to retain order.
+ *
+ * A HashSet
is used for the set behaviour.
+ *
+ * NOTE: If the list contains duplicates, the duplicates are removed,
+ * altering the specified list.
+ *
+ * @param list the list to decorate, must not be null
+ * @throws IllegalArgumentException if list is null
+ */
+ public static ListOrderedSet decorate(List list) {
+ if (list == null) {
+ throw new IllegalArgumentException("List must not be null");
+ }
+ Set set = new HashSet(list);
+ list.retainAll(set);
+
+ return new ListOrderedSet(set, list);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructs a new empty ListOrderedSet
using
+ * a HashSet
and an ArrayList
internally.
+ *
+ * @since Commons Collections 3.1
+ */
+ public ListOrderedSet() {
+ super(new HashSet());
+ setOrder = new ArrayList();
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param set the set to decorate, must not be null
+ * @throws IllegalArgumentException if set is null
+ */
+ protected ListOrderedSet(Set set) {
+ super(set);
+ setOrder = new ArrayList(set);
+ }
+
+ /**
+ * Constructor that wraps (not copies) the Set and specifies the list to use.
+ *
+ * The set and list must both be correctly initialised to the same elements. + * + * @param set the set to decorate, must not be null + * @param list the list to decorate, must not be null + * @throws IllegalArgumentException if set or list is null + */ + protected ListOrderedSet(Set set, List list) { + super(set); + if (list == null) { + throw new IllegalArgumentException("List must not be null"); + } + setOrder = list; + } + + //----------------------------------------------------------------------- + /** + * Gets an unmodifiable view of the order of the Set. + * + * @return an unmodifiable list view + */ +//p:ûбҪתList +// public List asList() { +// return UnmodifiableList.decorate(setOrder); +// } + + //----------------------------------------------------------------------- + public void clear() { + collection.clear(); + setOrder.clear(); + } + + public Iterator iterator() { + return new OrderedSetIterator(setOrder.iterator(), collection); + } + + public boolean add(Object object) { + if (collection.contains(object)) { + // re-adding doesn't change order + return collection.add(object); + } else { + // first add, so add to both set and list + boolean result = collection.add(object); + setOrder.add(object); + return result; + } + } + + public boolean addAll(Collection coll) { + boolean result = false; + for (Iterator it = coll.iterator(); it.hasNext();) { + Object object = it.next(); + result = result | add(object); + } + return result; + } + + public boolean remove(Object object) { + boolean result = collection.remove(object); + setOrder.remove(object); + return result; + } + + public boolean removeAll(Collection coll) { + boolean result = false; + for (Iterator it = coll.iterator(); it.hasNext();) { + Object object = it.next(); + result = result | remove(object); + } + return result; + } + + public boolean retainAll(Collection coll) { + boolean result = collection.retainAll(coll); + if (result == false) { + return false; + } else if (collection.size() == 0) { + setOrder.clear(); + } else { + for (Iterator it = setOrder.iterator(); it.hasNext();) { + Object object = it.next(); + if (collection.contains(object) == false) { + it.remove(); + } + } + } + return result; + } + + public Object[] toArray() { + return setOrder.toArray(); + } + + public Object[] toArray(Object a[]) { + return setOrder.toArray(a); + } + + //----------------------------------------------------------------------- + public Object get(int index) { + return setOrder.get(index); + } + + public int indexOf(Object object) { + return setOrder.indexOf(object); + } + + public void add(int index, Object object) { + if (contains(object) == false) { + collection.add(object); + setOrder.add(index, object); + } + } + + public boolean addAll(int index, Collection coll) { + boolean changed = false; + for (Iterator it = coll.iterator(); it.hasNext();) { + Object object = it.next(); + if (contains(object) == false) { + collection.add(object); + setOrder.add(index, object); + index++; + changed = true; + } + } + return changed; + } + + public Object remove(int index) { + Object obj = setOrder.remove(index); + remove(obj); + return obj; + } + + /** + * Uses the underlying List's toString so that order is achieved. + * This means that the decorated Set's toString is not used, so + * any custom toStrings will be ignored. + */ + // Fortunately List.toString and Set.toString look the same + public String toString() { + return setOrder.toString(); + } + + //----------------------------------------------------------------------- + /** + * Internal iterator handle remove. + */ + static class OrderedSetIterator extends AbstractIteratorDecorator { + + /** Object we iterate on */ + protected final Collection set; + /** Last object retrieved */ + protected Object last; + + private OrderedSetIterator(Iterator iterator, Collection set) { + super(iterator); + this.set = set; + } + + public Object next() { + last = iterator.next(); + return last; + } + + public void remove() { + set.remove(last); + iterator.remove(); + last = null; + } + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/JobRunShell.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/JobRunShell.java new file mode 100644 index 000000000..c5af60faa --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/JobRunShell.java @@ -0,0 +1,431 @@ + +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.core; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import com.fr.third.org.quartz.Job; +import com.fr.third.org.quartz.JobDetail; +import com.fr.third.org.quartz.JobExecutionContext; +import com.fr.third.org.quartz.JobExecutionException; +import com.fr.third.org.quartz.JobPersistenceException; +import com.fr.third.org.quartz.Scheduler; +import com.fr.third.org.quartz.SchedulerException; +import com.fr.third.org.quartz.Trigger; +import com.fr.third.org.quartz.spi.TriggerFiredBundle; + +/** + *
+ * JobRunShell instances are responsible for providing the 'safe' environment
+ * for Job
s to run in, and for performing all of the work of
+ * executing the Job
, catching ANY thrown exceptions, updating
+ * the Trigger
with the Job
's completion code,
+ * etc.
+ *
+ * A JobRunShell
instance is created by a JobRunShellFactory
+ * on behalf of the QuartzSchedulerThread
which then runs the
+ * shell in a thread from the configured ThreadPool
when the
+ * scheduler determines that a Job
has been triggered.
+ *
+ * Create a JobRunShell instance with the given settings. + *
+ * + * @param jobRunShellFactory + * A handle to theJobRunShellFactory
that produced
+ * this JobRunShell
.
+ * @param scheduler
+ * The Scheduler
instance that should be made
+ * available within the JobExecutionContext
.
+ * @param schdCtxt
+ * the SchedulingContext
that should be used by the
+ * JobRunShell
when making updates to the JobStore
.
+ */
+ public JobRunShell(JobRunShellFactory jobRunShellFactory,
+ Scheduler scheduler, SchedulingContext schdCtxt) {
+ this.jobRunShellFactory = jobRunShellFactory;
+ this.scheduler = scheduler;
+ this.schdCtxt = schdCtxt;
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ protected Log getLog() {
+ return log;
+ }
+
+ public void initialize(QuartzScheduler qs, TriggerFiredBundle firedBundle)
+ throws SchedulerException {
+ this.qs = qs;
+
+ Job job = null;
+ JobDetail jobDetail = firedBundle.getJobDetail();
+
+ try {
+ job = qs.getJobFactory().newJob(firedBundle);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError(
+ "An error occured instantiating job to be executed. job= '"
+ + jobDetail.getFullName() + "'", se);
+ throw se;
+ } catch (Throwable ncdfe) { // such as NoClassDefFoundError
+ SchedulerException se = new SchedulerException(
+ "Problem instantiating class '"
+ + jobDetail.getJobClass().getName() + "' - ", ncdfe);
+ qs.notifySchedulerListenersError(
+ "An error occured instantiating job to be executed. job= '"
+ + jobDetail.getFullName() + "'", se);
+ throw se;
+ }
+
+ this.jec = new JobExecutionContext(scheduler, firedBundle, job);
+ }
+
+ public void requestShutdown() {
+ shutdownRequested = true;
+ }
+
+ public void run() {
+ try {
+ Trigger trigger = jec.getTrigger();
+ JobDetail jobDetail = jec.getJobDetail();
+
+ do {
+
+ JobExecutionException jobExEx = null;
+ Job job = jec.getJobInstance();
+
+ try {
+ begin();
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError("Error executing Job ("
+ + jec.getJobDetail().getFullName()
+ + ": couldn't begin execution.", se);
+ break;
+ }
+
+ // notify job & trigger listeners...
+ try {
+ if (!notifyListenersBeginning(jec)) {
+ break;
+ }
+ } catch(VetoedException ve) {
+ try {
+ int instCode = trigger.executionComplete(jec, null);
+ try {
+ qs.notifyJobStoreJobVetoed(schdCtxt, trigger, jobDetail, instCode);
+ } catch(JobPersistenceException jpe) {
+ vetoedJobRetryLoop(trigger, jobDetail, instCode);
+ }
+ complete(true);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError("Error during veto of Job ("
+ + jec.getJobDetail().getFullName()
+ + ": couldn't finalize execution.", se);
+ }
+ break;
+ }
+
+ long startTime = System.currentTimeMillis();
+ long endTime = startTime;
+
+ // execute the job
+ try {
+ log.debug("Calling execute on job " + jobDetail.getFullName());
+ job.execute(jec);
+ endTime = System.currentTimeMillis();
+ } catch (JobExecutionException jee) {
+ endTime = System.currentTimeMillis();
+ jobExEx = jee;
+ getLog().info("Job " + jobDetail.getFullName() +
+ " threw a JobExecutionException: ", jobExEx);
+ } catch (Throwable e) {
+ endTime = System.currentTimeMillis();
+ getLog().error("Job " + jobDetail.getFullName() +
+ " threw an unhandled Exception: ", e);
+ SchedulerException se = new SchedulerException(
+ "Job threw an unhandled exception.", e);
+ se.setErrorCode(SchedulerException.ERR_JOB_EXECUTION_THREW_EXCEPTION);
+ qs.notifySchedulerListenersError("Job ("
+ + jec.getJobDetail().getFullName()
+ + " threw an exception.", se);
+ jobExEx = new JobExecutionException(se, false);
+ jobExEx.setErrorCode(JobExecutionException.ERR_JOB_EXECUTION_THREW_EXCEPTION);
+ }
+
+ jec.setJobRunTime(endTime - startTime);
+
+ // notify all job listeners
+ if (!notifyJobListenersComplete(jec, jobExEx)) {
+ break;
+ }
+
+ int instCode = Trigger.INSTRUCTION_NOOP;
+
+ // update the trigger
+ try {
+ instCode = trigger.executionComplete(jec, jobExEx);
+ } catch (Exception e) {
+ // If this happens, there's a bug in the trigger...
+ SchedulerException se = new SchedulerException(
+ "Trigger threw an unhandled exception.", e);
+ se.setErrorCode(SchedulerException.ERR_TRIGGER_THREW_EXCEPTION);
+ qs.notifySchedulerListenersError(
+ "Please report this error to the Quartz developers.",
+ se);
+ }
+
+ // notify all trigger listeners
+ if (!notifyTriggerListenersComplete(jec, instCode)) {
+ break;
+ }
+
+ // update job/trigger or re-execute job
+ if (instCode == Trigger.INSTRUCTION_RE_EXECUTE_JOB) {
+ jec.incrementRefireCount();
+ try {
+ complete(false);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError("Error executing Job ("
+ + jec.getJobDetail().getFullName()
+ + ": couldn't finalize execution.", se);
+ }
+ continue;
+ }
+
+ try {
+ complete(true);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError("Error executing Job ("
+ + jec.getJobDetail().getFullName()
+ + ": couldn't finalize execution.", se);
+ continue;
+ }
+
+ try {
+ qs.notifyJobStoreJobComplete(schdCtxt, trigger, jobDetail,
+ instCode);
+ } catch (JobPersistenceException jpe) {
+ qs.notifySchedulerListenersError(
+ "An error occured while marking executed job complete. job= '"
+ + jobDetail.getFullName() + "'", jpe);
+ if (!completeTriggerRetryLoop(trigger, jobDetail, instCode)) {
+ return;
+ }
+ }
+
+ break;
+ } while (true);
+
+ } finally {
+ jobRunShellFactory.returnJobRunShell(this);
+ }
+ }
+
+ protected void begin() throws SchedulerException {
+ }
+
+ protected void complete(boolean successfulExecution)
+ throws SchedulerException {
+ }
+
+ public void passivate() {
+ jec = null;
+ qs = null;
+ }
+
+ private boolean notifyListenersBeginning(JobExecutionContext jec) throws VetoedException {
+
+ boolean vetoed = false;
+
+ // notify all trigger listeners
+ try {
+ vetoed = qs.notifyTriggerListenersFired(jec);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError(
+ "Unable to notify TriggerListener(s) while firing trigger "
+ + "(Trigger and Job will NOT be fired!). trigger= "
+ + jec.getTrigger().getFullName() + " job= "
+ + jec.getJobDetail().getFullName(), se);
+
+ return false;
+ }
+
+ if(vetoed) {
+ try {
+ qs.notifyJobListenersWasVetoed(jec);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError(
+ "Unable to notify JobListener(s) of vetoed execution " +
+ "while firing trigger (Trigger and Job will NOT be " +
+ "fired!). trigger= "
+ + jec.getTrigger().getFullName() + " job= "
+ + jec.getJobDetail().getFullName(), se);
+
+ }
+ throw new VetoedException();
+ }
+
+ // notify all job listeners
+ try {
+ qs.notifyJobListenersToBeExecuted(jec);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError(
+ "Unable to notify JobListener(s) of Job to be executed: "
+ + "(Job will NOT be executed!). trigger= "
+ + jec.getTrigger().getFullName() + " job= "
+ + jec.getJobDetail().getFullName(), se);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean notifyJobListenersComplete(JobExecutionContext jec,
+ JobExecutionException jobExEx) {
+ try {
+ qs.notifyJobListenersWasExecuted(jec, jobExEx);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError(
+ "Unable to notify JobListener(s) of Job that was executed: "
+ + "(error will be ignored). trigger= "
+ + jec.getTrigger().getFullName() + " job= "
+ + jec.getJobDetail().getFullName(), se);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean notifyTriggerListenersComplete(JobExecutionContext jec,
+ int instCode) {
+ try {
+ qs.notifyTriggerListenersComplete(jec, instCode);
+
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError(
+ "Unable to notify TriggerListener(s) of Job that was executed: "
+ + "(error will be ignored). trigger= "
+ + jec.getTrigger().getFullName() + " job= "
+ + jec.getJobDetail().getFullName(), se);
+
+ return false;
+ }
+ if (jec.getTrigger().getNextFireTime() == null) {
+ qs.notifySchedulerListenersFinalized(jec.getTrigger());
+ }
+
+ return true;
+ }
+
+ public boolean completeTriggerRetryLoop(Trigger trigger,
+ JobDetail jobDetail, int instCode) {
+ while (!shutdownRequested) {
+ try {
+ Thread.sleep(5 * 1000L); // retry every 5 seconds (the db
+ // connection must be failed)
+ qs.notifyJobStoreJobComplete(schdCtxt, trigger, jobDetail,
+ instCode);
+ return true;
+ } catch (JobPersistenceException jpe) {
+ qs.notifySchedulerListenersError(
+ "An error occured while marking executed job complete. job= '"
+ + jobDetail.getFullName() + "'", jpe);
+ } catch (InterruptedException ignore) {
+ }
+ }
+ return false;
+ }
+
+ public boolean vetoedJobRetryLoop(Trigger trigger, JobDetail jobDetail, int instCode) {
+ while (!shutdownRequested) {
+ try {
+ Thread.sleep(5 * 1000L); // retry every 5 seconds (the db
+ // connection must be failed)
+ qs.notifyJobStoreJobVetoed(schdCtxt, trigger, jobDetail, instCode);
+ return true;
+ } catch (JobPersistenceException jpe) {
+ qs.notifySchedulerListenersError(
+ "An error occured while marking executed job vetoed. job= '"
+ + jobDetail.getFullName() + "'", jpe);
+ } catch (InterruptedException ignore) {
+ }
+ }
+ return false;
+ }
+
+ class VetoedException extends Exception {
+ public VetoedException() {
+ }
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/JobRunShellFactory.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/JobRunShellFactory.java
new file mode 100644
index 000000000..c6d781a87
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/JobRunShellFactory.java
@@ -0,0 +1,82 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.core;
+
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerConfigException;
+import com.fr.third.org.quartz.SchedulerException;
+
+/**
+ *
+ * Responsible for creating the instances of {@link JobRunShell}
+ * to be used within the
+ * Although this interface looks a lot like an 'object pool', implementations
+ * do not have to support the re-use of instances. If an implementation does
+ * not wish to pool instances, then the borrowJobRunShell()
+ * method would simply create a new instance, and the returnJobRunShell
+ *
method would do nothing.
+ *
+ * Initialize the factory, providing a handle to the Scheduler
+ * that should be made available within the JobRunShell
and
+ * the JobExecutionCOntext
s within it, and a handle to the
+ * SchedulingContext
that the shell will use in its own
+ * operations with the JobStore
.
+ *
+ * Called by the {@link com.fr.third.org.quartz.core.QuartzSchedulerThread}
+ * to obtain instances of {@link JobRunShell}
.
+ *
+ * Called by the {@link com.fr.third.org.quartz.core.QuartzSchedulerThread}
+ * to return instances of {@link JobRunShell}
.
+ *
+ * This is the heart of Quartz, an indirect implementation of the {@link com.fr.third.org.quartz.Scheduler}
+ * interface, containing methods to schedule {@link com.fr.third.org.quartz.Job}
s,
+ * register {@link com.fr.third.org.quartz.JobListener}
instances, etc.
+ *
+ * Create a QuartzScheduler
with the given configuration
+ * properties.
+ *
+ * Bind the scheduler to an RMI registry. + *
+ */ + private void bind() throws RemoteException { + String host = resources.getRMIRegistryHost(); + // don't export if we're not configured to do so... + if (host == null || host.length() == 0) { + return; + } + + RemotableQuartzScheduler exportable = null; + + if(resources.getRMIServerPort() > 0) { + exportable = (RemotableQuartzScheduler) UnicastRemoteObject + .exportObject(this, resources.getRMIServerPort()); + } else { + exportable = (RemotableQuartzScheduler) UnicastRemoteObject + .exportObject(this); + } + + Registry registry = null; + + if (resources.getRMICreateRegistryStrategy().equals( + QuartzSchedulerResources.CREATE_REGISTRY_AS_NEEDED)) { + try { + // First try to get an existing one, instead of creating it, + // since if + // we're in a web-app being 'hot' re-depoloyed, then the JVM + // still + // has the registry that we created above the first time... + registry = LocateRegistry.getRegistry(resources + .getRMIRegistryPort()); + registry.list(); + } catch (Exception e) { + registry = LocateRegistry.createRegistry(resources + .getRMIRegistryPort()); + } + } else if (resources.getRMICreateRegistryStrategy().equals( + QuartzSchedulerResources.CREATE_REGISTRY_ALWAYS)) { + try { + registry = LocateRegistry.createRegistry(resources + .getRMIRegistryPort()); + } catch (Exception e) { + // Fall back to an existing one, instead of creating it, since + // if + // we're in a web-app being 'hot' re-depoloyed, then the JVM + // still + // has the registry that we created above the first time... + registry = LocateRegistry.getRegistry(resources + .getRMIRegistryPort()); + } + } else { + registry = LocateRegistry.getRegistry(resources + .getRMIRegistryHost(), resources.getRMIRegistryPort()); + } + + String bindName = resources.getRMIBindName(); + + registry.rebind(bindName, exportable); + + getLog().info("Scheduler bound to RMI registry under name '" + bindName + "'"); + } + + /** + *+ * Un-bind the scheduler from an RMI registry. + *
+ */ + private void unBind() throws RemoteException { + String host = resources.getRMIRegistryHost(); + // don't un-export if we're not configured to do so... + if (host == null || host.length() == 0) { + return; + } + + Registry registry = LocateRegistry.getRegistry(resources + .getRMIRegistryHost(), resources.getRMIRegistryPort()); + + String bindName = resources.getRMIBindName(); + + try { + registry.unbind(bindName); + UnicastRemoteObject.unexportObject(this, true); + } catch (java.rmi.NotBoundException nbe) { + } + + getLog().info("Scheduler un-bound from name '" + bindName + "' in RMI registry"); + } + + /** + *
+ * Returns the name of the QuartzScheduler
.
+ *
+ * Returns the instance Id of the QuartzScheduler
.
+ *
+ * Returns the name of the QuartzScheduler
.
+ *
+ * Returns the SchedulerContext
of the Scheduler
.
+ *
+ * Starts the QuartzScheduler
's threads that fire {@link com.fr.third.org.quartz.Trigger}s
.
+ *
+ * All {@link com.fr.third.org.quartz.Trigger}s
that have misfired will
+ * be passed to the appropriate TriggerListener(s).
+ *
+ * Temporarily halts the QuartzScheduler
's firing of {@link com.fr.third.org.quartz.Trigger}s
.
+ *
+ * The scheduler is not destroyed, and can be re-started at any time. + *
+ */ + public void standby() { + schedThread.togglePause(true); + getLog().info( + "Scheduler " + resources.getUniqueIdentifier() + " paused."); + } + + /** + *
+ * Reports whether the Scheduler
is paused.
+ *
+ * Halts the QuartzScheduler
's firing of {@link com.fr.third.org.quartz.Trigger}s
,
+ * and cleans up all resources associated with the QuartzScheduler.
+ * Equivalent to shutdown(false)
.
+ *
+ * The scheduler cannot be re-started. + *
+ */ + public void shutdown() { + shutdown(false); + } + + /** + *
+ * Halts the QuartzScheduler
's firing of {@link com.fr.third.org.quartz.Trigger}s
,
+ * and cleans up all resources associated with the QuartzScheduler.
+ *
+ * The scheduler cannot be re-started. + *
+ * + * @param waitForJobsToComplete + * iftrue
the scheduler will not allow this method
+ * to return until all currently executing jobs have completed.
+ */
+ public void shutdown(boolean waitForJobsToComplete) {
+
+ if(shuttingDown || closed) {
+ return;
+ }
+
+ shuttingDown = true;
+
+ getLog().info(
+ "Scheduler " + resources.getUniqueIdentifier()
+ + " shutting down.");
+ standby();
+
+
+ schedThread.halt();
+
+ resources.getThreadPool().shutdown(waitForJobsToComplete);
+
+ if (waitForJobsToComplete) {
+ while (jobMgr.getNumJobsCurrentlyExecuting() > 0) {
+ try {
+ Thread.sleep(100);
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ // Scheduler thread may have be waiting for the fire time of an acquired
+ // trigger and need time to release the trigger once halted, so make sure
+ // the thread is dead before continuing to shutdown the job store.
+ try {
+ schedThread.join();
+ } catch (InterruptedException ignore) {
+ }
+
+ closed = true;
+
+ resources.getJobStore().shutdown();
+
+ notifySchedulerListenersShutdown();
+
+ shutdownPlugins();
+
+ SchedulerRepository.getInstance().remove(resources.getName());
+
+ holdToPreventGC.clear();
+
+ try {
+ unBind();
+ } catch (RemoteException re) {
+ }
+
+ if (resources.getJMXExport()) {
+ try {
+ unregisterJMX();
+ } catch (Exception e) {
+ }
+ }
+
+ getLog().info(
+ "Scheduler " + resources.getUniqueIdentifier()
+ + " shutdown complete.");
+ }
+
+ /**
+ *
+ * Reports whether the Scheduler
has been shutdown.
+ *
+ * Return a list of JobExecutionContext
objects that
+ * represent all currently executing Jobs in this Scheduler instance.
+ *
+ * This method is not cluster aware. That is, it will only return Jobs + * currently executing in this Scheduler instance, not across the entire + * cluster. + *
+ * + *+ * Note that the list returned is an 'instantaneous' snap-shot, and that as + * soon as it's returned, the true list of executing jobs may be different. + *
+ */ + public List getCurrentlyExecutingJobs() { + return jobMgr.getExecutingJobs(); + } + + /////////////////////////////////////////////////////////////////////////// + /// + /// Scheduling-related Methods + /// + /////////////////////////////////////////////////////////////////////////// + + /** + *
+ * Add the {@link com.fr.third.org.quartz.Job}
identified by the given
+ * {@link com.fr.third.org.quartz.JobDetail}
to the Scheduler, and
+ * associate the given {@link com.fr.third.org.quartz.Trigger}
with it.
+ *
+ * If the given Trigger does not reference any Job
, then it
+ * will be set to reference the Job passed with it into this method.
+ *
+ * Schedule the given {@link com.fr.third.org.quartz.Trigger}
with the
+ * Job
identified by the Trigger
's settings.
+ *
+ * Add the given Job
to the Scheduler - with no associated
+ * Trigger
. The Job
will be 'dormant' until
+ * it is scheduled with a Trigger
, or Scheduler.triggerJob()
+ * is called for it.
+ *
+ * The Job
must by definition be 'durable', if it is not,
+ * SchedulerException will be thrown.
+ *
replace
is false
.
+ */
+ public void addJob(SchedulingContext ctxt, JobDetail jobDetail,
+ boolean replace) throws SchedulerException {
+ validateState();
+
+ if (!jobDetail.isDurable() && !replace) {
+ throw new SchedulerException(
+ "Jobs added with no trigger must be durable.",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+
+ resources.getJobStore().storeJob(ctxt, jobDetail, replace);
+ }
+
+ /**
+ *
+ * Delete the identified Job
from the Scheduler - and any
+ * associated Trigger
s.
+ *
+ * Remove the indicated {@link com.fr.third.org.quartz.Trigger}
from the
+ * scheduler.
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Trigger}
with the
+ * given name, and store the new given one - which must be associated
+ * with the same job.
+ *
Trigger
to be removed.
+ * @param groupName
+ * The group name of the Trigger
to be removed.
+ * @param newTrigger
+ * The new Trigger
to be stored.
+ * @return null
if a Trigger
with the given
+ * name & group was not found and removed from the store, otherwise
+ * the first fire time of the newly scheduled trigger.
+ */
+ public Date rescheduleJob(SchedulingContext ctxt, String triggerName,
+ String groupName, Trigger newTrigger) throws SchedulerException {
+ validateState();
+
+ if(groupName == null) {
+ groupName = Scheduler.DEFAULT_GROUP;
+ }
+
+ newTrigger.validate();
+
+ Calendar cal = null;
+ if (newTrigger.getCalendarName() != null) {
+ cal = resources.getJobStore().retrieveCalendar(ctxt,
+ newTrigger.getCalendarName());
+ }
+ Date ft = newTrigger.computeFirstFireTime(cal);
+
+ if (ft == null) {
+ throw new SchedulerException(
+ "Based on configured schedule, the given trigger will never fire.",
+ SchedulerException.ERR_CLIENT_ERROR);
+ }
+
+ if (resources.getJobStore().replaceTrigger(ctxt, triggerName, groupName, newTrigger)) {
+ notifySchedulerThread(newTrigger.getNextFireTime().getTime());
+ notifySchedulerListenersUnschduled(triggerName, groupName);
+ notifySchedulerListenersSchduled(newTrigger);
+ } else {
+ return null;
+ }
+
+ return ft;
+
+ }
+
+
+ private String newTriggerId() {
+ long r = random.nextLong();
+ if (r < 0) {
+ r = -r;
+ }
+ return "MT_"
+ + Long.toString(r, 30 + (int) (System.currentTimeMillis() % 7));
+ }
+
+ /**
+ *
+ * Trigger the identified {@link com.fr.third.org.quartz.Job}
(execute it
+ * now) - with a non-volatile trigger.
+ *
+ * Trigger the identified {@link com.fr.third.org.quartz.Job}
(execute it
+ * now) - with a volatile trigger.
+ *
+ * Pause the {@link Trigger}
with the given name.
+ *
+ * Pause all of the {@link Trigger}s
in the given group.
+ *
+ * Pause the {@link com.fr.third.org.quartz.JobDetail}
with the given
+ * name - by pausing all of its current Trigger
s.
+ *
+ * Pause all of the {@link com.fr.third.org.quartz.JobDetail}s
in the
+ * given group - by pausing all of their Trigger
s.
+ *
+ * Resume (un-pause) the {@link Trigger}
with the given
+ * name.
+ *
+ * If the Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link Trigger}s
in the
+ * given group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) the {@link com.fr.third.org.quartz.JobDetail}
with
+ * the given name.
+ *
+ * If any of the Job
'sTrigger
s missed one
+ * or more fire-times, then the Trigger
's misfire
+ * instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link com.fr.third.org.quartz.JobDetail}s
+ * in the given group.
+ *
+ * If any of the Job
s had Trigger
s that
+ * missed one or more fire-times, then the Trigger
's
+ * misfire instruction will be applied.
+ *
+ * Pause all triggers - equivalent of calling pauseTriggerGroup(group)
+ * on every group.
+ *
+ * When resumeAll()
is called (to un-pause), trigger misfire
+ * instructions WILL be applied.
+ *
+ * Resume (un-pause) all triggers - equivalent of calling resumeTriggerGroup(group)
+ * on every group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Get the names of all known {@link com.fr.third.org.quartz.Job}
groups.
+ *
+ * Get the names of all the {@link com.fr.third.org.quartz.Job}s
in the
+ * given group.
+ *
+ * Get all {@link Trigger}
s that are associated with the
+ * identified {@link com.fr.third.org.quartz.JobDetail}
.
+ *
+ * Get the names of all known {@link com.fr.third.org.quartz.Trigger}
+ * groups.
+ *
+ * Get the names of all the {@link com.fr.third.org.quartz.Trigger}s
in
+ * the given group.
+ *
+ * Get the {@link JobDetail}
for the Job
+ * instance with the given name and group.
+ *
+ * Get the {@link Trigger}
instance with the given name and
+ * group.
+ *
+ * Get the current state of the identified {@link Trigger}
.
+ *
+ * Add (register) the given Calendar
to the Scheduler.
+ *
replace
is
+ * false
.
+ */
+ public void addCalendar(SchedulingContext ctxt, String calName,
+ Calendar calendar, boolean replace, boolean updateTriggers) throws SchedulerException {
+ validateState();
+
+ resources.getJobStore().storeCalendar(ctxt, calName, calendar, replace, updateTriggers);
+ }
+
+ /**
+ *
+ * Delete the identified Calendar
from the Scheduler.
+ *
+ * Get the {@link Calendar}
instance with the given name.
+ *
+ * Get the names of all registered {@link Calendar}s
.
+ *
+ * Add the given {@link com.fr.third.org.quartz.JobListener}
to the
+ * Scheduler
'sglobal list.
+ *
+ * Listeners in the 'global' list receive notification of execution events
+ * for ALL {@link com.fr.third.org.quartz.Job}
s.
+ *
+ * Add the given {@link com.fr.third.org.quartz.JobListener}
to the
+ * Scheduler
's list, of registered JobListener
s.
+ */
+ public void addJobListener(JobListener jobListener) {
+ if (jobListener.getName() == null
+ || jobListener.getName().length() == 0) {
+ throw new IllegalArgumentException(
+ "JobListener name cannot be empty.");
+ }
+
+ synchronized (jobListeners) {
+ jobListeners.put(jobListener.getName(), jobListener);
+ }
+ }
+
+ /**
+ *
+ * Remove the given {@link com.fr.third.org.quartz.JobListener}
from the
+ * Scheduler
's list of global listeners.
+ *
{@link #removeGlobalJobListener(String)}
+ */
+ public boolean removeGlobalJobListener(JobListener jobListener) {
+ return removeGlobalJobListener((jobListener == null) ? null : jobListener.getName());
+ }
+
+ /**
+ *
+ * Remove the identifed {@link JobListener}
from the Scheduler
's
+ * list of global listeners.
+ *
+ * Remove the identifed {@link com.fr.third.org.quartz.JobListener}
from
+ * the Scheduler
's list of registered listeners.
+ *
+ * Get a List containing all of the {@link com.fr.third.org.quartz.JobListener}
+ * s in the Scheduler
'sglobal list.
+ *
+ * Get a Set containing the names of all the non-global{@link com.fr.third.org.quartz.JobListener}
+ * s registered with the Scheduler
.
+ *
+ * Get the global{@link com.fr.third.org.quartz.JobListener}
+ * that has the given name.
+ *
+ * Get the non-global{@link com.fr.third.org.quartz.JobListener}
+ * that has the given name.
+ *
+ * Add the given {@link com.fr.third.org.quartz.TriggerListener}
to the
+ * Scheduler
'sglobal list.
+ *
+ * Listeners in the 'global' list receive notification of execution events
+ * for ALL {@link com.fr.third.org.quartz.Trigger}
s.
+ *
+ * Add the given {@link com.fr.third.org.quartz.TriggerListener}
to the
+ * Scheduler
's list, of registered TriggerListener
s.
+ */
+ public void addTriggerListener(TriggerListener triggerListener) {
+ if (triggerListener.getName() == null
+ || triggerListener.getName().length() == 0) {
+ throw new IllegalArgumentException(
+ "TriggerListener name cannot be empty.");
+ }
+
+ synchronized (triggerListeners) {
+ triggerListeners.put(triggerListener.getName(), triggerListener);
+ }
+ }
+
+ /**
+ *
+ * Remove the given {@link com.fr.third.org.quartz.TriggerListener}
from
+ * the Scheduler
's list of global listeners.
+ *
{@link #removeGlobalTriggerListener(String)}
+ */
+ public boolean removeGlobalTriggerListener(TriggerListener triggerListener) {
+ return removeGlobalTriggerListener((triggerListener == null) ? null : triggerListener.getName());
+ }
+
+ /**
+ *
+ * Remove the identifed {@link TriggerListener}
from the Scheduler
's
+ * list of global listeners.
+ *
+ * Remove the identifed {@link com.fr.third.org.quartz.TriggerListener}
+ * from the Scheduler
's list of registered listeners.
+ *
+ * Get a list containing all of the {@link com.fr.third.org.quartz.TriggerListener}
+ * s in the Scheduler
'sglobal list.
+ *
+ * Get a Set containing the names of all the non-global{@link com.fr.third.org.quartz.TriggerListener}
+ * s registered with the Scheduler
.
+ *
+ * Get the global{@link TriggerListener}
that
+ * has the given name.
+ *
+ * Get the non-global{@link com.fr.third.org.quartz.TriggerListener}
+ * that has the given name.
+ *
+ * Register the given {@link SchedulerListener}
with the
+ * Scheduler
.
+ *
+ * Remove the given {@link SchedulerListener}
from the
+ * Scheduler
.
+ *
+ * Get a List containing all of the {@link SchedulerListener}
+ * s registered with the Scheduler
.
+ *
+ * This method is not cluster aware. That is, it will only interrupt + * instances of the identified InterruptableJob currently executing in this + * Scheduler instance, not across the entire cluster. + *
+ * + * @see com.fr.third.org.quartz.core.RemotableQuartzScheduler#interrupt(com.fr.third.org.quartz.core.SchedulingContext, java.lang.String, java.lang.String) + */ + public boolean interrupt(SchedulingContext ctxt, String jobName, String groupName) throws UnableToInterruptJobException { + + if(groupName == null) { + groupName = Scheduler.DEFAULT_GROUP; + } + + List jobs = getCurrentlyExecutingJobs(); + java.util.Iterator it = jobs.iterator(); + + JobExecutionContext jec = null; + JobDetail jobDetail = null; + Job job = null; + + boolean interrupted = false; + + while (it.hasNext()) { + jec = (JobExecutionContext)it.next(); + jobDetail = jec.getJobDetail(); + if (jobName.equals(jobDetail.getName()) + && groupName.equals(jobDetail.getGroup())){ + job = jec.getJobInstance(); + if (job instanceof InterruptableJob) { + ((InterruptableJob)job).interrupt(); + interrupted = true; + } else { + throw new UnableToInterruptJobException( + "Job '" + + jobName + + "' of group '" + + groupName + + "' can not be interrupted, since it does not implement " + + InterruptableJob.class.getName()); + + } + } + } + + return interrupted; + } + + private void shutdownPlugins() { + java.util.Iterator itr = resources.getSchedulerPlugins().iterator(); + while (itr.hasNext()) { + SchedulerPlugin plugin = (SchedulerPlugin) itr.next(); + plugin.shutdown(); + } + } + + private void startPlugins() { + java.util.Iterator itr = resources.getSchedulerPlugins().iterator(); + while (itr.hasNext()) { + SchedulerPlugin plugin = (SchedulerPlugin) itr.next(); + plugin.start(); + } + } + +} + +///////////////////////////////////////////////////////////////////////////// +// +// ErrorLogger - Scheduler Listener Class +// +///////////////////////////////////////////////////////////////////////////// + +class ErrorLogger extends SchedulerListenerSupport { + ErrorLogger() { + } + + public void schedulerError(String msg, SchedulerException cause) { + getLog().error(msg, cause); + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// ExecutingJobsManager - Job Listener Class +// +///////////////////////////////////////////////////////////////////////////// + +class ExecutingJobsManager implements JobListener { + HashMap executingJobs = new HashMap(); + + int numJobsFired = 0; + + ExecutingJobsManager() { + } + + public String getName() { + return getClass().getName(); + } + + public int getNumJobsCurrentlyExecuting() { + synchronized (executingJobs) { + return executingJobs.size(); + } + } + + public void jobToBeExecuted(JobExecutionContext context) { + numJobsFired++; + + synchronized (executingJobs) { + executingJobs + .put(context.getTrigger().getFireInstanceId(), context); + } + } + + public void jobWasExecuted(JobExecutionContext context, + JobExecutionException jobException) { + synchronized (executingJobs) { + executingJobs.remove(context.getTrigger().getFireInstanceId()); + } + } + + public int getNumJobsFired() { + return numJobsFired; + } + + public List getExecutingJobs() { + synchronized (executingJobs) { + return java.util.Collections.unmodifiableList(new ArrayList( + executingJobs.values())); + } + } + + public void jobExecutionVetoed(JobExecutionContext context) { + + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/QuartzSchedulerResources.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/QuartzSchedulerResources.java new file mode 100644 index 000000000..113626c3e --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/QuartzSchedulerResources.java @@ -0,0 +1,519 @@ + +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.core; + +import java.util.ArrayList; +import java.util.List; + +import com.fr.third.org.quartz.spi.JobStore; +import com.fr.third.org.quartz.spi.SchedulerPlugin; +import com.fr.third.org.quartz.spi.ThreadPool; + +/** + *
+ * Contains all of the resources (JobStore
,ThreadPool
,
+ * etc.) necessary to create a {@link QuartzScheduler}
instance.
+ *
+ * Create an instance with no properties initialized. + *
+ */ + public QuartzSchedulerResources() { + // do nothing... + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *
+ * Get the name for the {@link QuartzScheduler}
.
+ *
+ * Set the name for the {@link QuartzScheduler}
.
+ *
+ * Get the instance Id for the {@link QuartzScheduler}
.
+ *
+ * Set the name for the {@link QuartzScheduler}
.
+ *
+ * Get the host name of the RMI Registry that the scheduler should export + * itself to. + *
+ */ + public String getRMIRegistryHost() { + return rmiRegistryHost; + } + + /** + *+ * Set the host name of the RMI Registry that the scheduler should export + * itself to. + *
+ */ + public void setRMIRegistryHost(String hostName) { + this.rmiRegistryHost = hostName; + } + + /** + *+ * Get the port number of the RMI Registry that the scheduler should export + * itself to. + *
+ */ + public int getRMIRegistryPort() { + return rmiRegistryPort; + } + + /** + *+ * Set the port number of the RMI Registry that the scheduler should export + * itself to. + *
+ */ + public void setRMIRegistryPort(int port) { + this.rmiRegistryPort = port; + } + + + /** + *+ * Get the port number the scheduler server will be bound to. + *
+ */ + public int getRMIServerPort() { + return rmiServerPort; + } + + /** + *+ * Set the port number the scheduler server will be bound to. + *
+ */ + public void setRMIServerPort(int port) { + this.rmiServerPort = port; + } + + /** + *+ * Get the setting of whether or not Quartz should create an RMI Registry, + * and if so, how. + *
+ */ + public String getRMICreateRegistryStrategy() { + return rmiCreateRegistryStrategy; + } + + /** + *
+ * Get the name for the {@link QuartzSchedulerThread}
.
+ *
+ * Set the name for the {@link QuartzSchedulerThread}
.
+ *
+ * Set whether or not Quartz should create an RMI Registry, and if so, how. + *
+ * + * @see #CREATE_REGISTRY_ALWAYS + * @see #CREATE_REGISTRY_AS_NEEDED + * @see #CREATE_REGISTRY_NEVER + */ + public void setRMICreateRegistryStrategy(String rmiCreateRegistryStrategy) { + if (rmiCreateRegistryStrategy == null + || rmiCreateRegistryStrategy.trim().length() == 0) { + rmiCreateRegistryStrategy = CREATE_REGISTRY_NEVER; + } else if (rmiCreateRegistryStrategy.equalsIgnoreCase("true")) { + rmiCreateRegistryStrategy = CREATE_REGISTRY_AS_NEEDED; + } else if (rmiCreateRegistryStrategy.equalsIgnoreCase("false")) { + rmiCreateRegistryStrategy = CREATE_REGISTRY_NEVER; + } else if (rmiCreateRegistryStrategy.equalsIgnoreCase(CREATE_REGISTRY_ALWAYS)) { + rmiCreateRegistryStrategy = CREATE_REGISTRY_ALWAYS; + } else if (rmiCreateRegistryStrategy.equalsIgnoreCase(CREATE_REGISTRY_AS_NEEDED)) { + rmiCreateRegistryStrategy = CREATE_REGISTRY_AS_NEEDED; + } else if (rmiCreateRegistryStrategy.equalsIgnoreCase(CREATE_REGISTRY_NEVER)) { + rmiCreateRegistryStrategy = CREATE_REGISTRY_NEVER; + } else { + throw new IllegalArgumentException( + "Faild to set RMICreateRegistryStrategy - strategy unknown: '" + + rmiCreateRegistryStrategy + "'"); + } + + this.rmiCreateRegistryStrategy = rmiCreateRegistryStrategy; + } + + /** + *
+ * Get the {@link ThreadPool}
for the {@link QuartzScheduler}
+ * to use.
+ *
+ * Set the {@link ThreadPool}
for the {@link QuartzScheduler}
+ * to use.
+ *
+ * Get the {@link JobStore}
for the {@link QuartzScheduler}
+ * to use.
+ *
+ * Set the {@link JobStore}
for the {@link QuartzScheduler}
+ * to use.
+ *
+ * Get the {@link JobRunShellFactory}
for the {@link QuartzScheduler}
+ * to use.
+ *
+ * Set the {@link JobRunShellFactory}
for the {@link QuartzScheduler}
+ * to use.
+ *
+ * Add the given {@link com.fr.third.org.quartz.spi.SchedulerPlugin}
for the
+ * {@link QuartzScheduler}
to use. This method expects the plugin's
+ * "initialize" method to be invoked externally (either before or after
+ * this method is called).
+ *
+ * Get the List
of all
+ * {@link com.fr.third.org.quartz.spi.SchedulerPlugin}
s for the
+ * {@link QuartzScheduler}
to use.
+ *
generateJMXObjectName.
+ *
+ * @see #generateJMXObjectName(String, String)
+ */
+ public String getJMXObjectName() {
+ return (jmxObjectName == null) ? generateJMXObjectName(name, instanceId) : jmxObjectName;
+ }
+
+ /**
+ * Set the name under which the QuartzScheduler should be registered with
+ * the local MBeanServer. If unset, defaults to the value calculated by
+ * generateJMXObjectName.
+ *
+ * @see #generateJMXObjectName(String, String)
+ */
+ public void setJMXObjectName(String jmxObjectName) {
+ this.jmxObjectName = jmxObjectName;
+ }
+
+ /**
+ * Create the name under which this scheduler should be registered in JMX.
+ *
+ * The name is composed as:
+ * quartz:type=QuartzScheduler,name=[schedName],instance=[schedInstId]
+ *
+ */
+ public static String generateJMXObjectName(String schedName, String schedInstId) {
+ return "quartz:type=QuartzScheduler" +
+ ",name=" + schedName +
+ ",instance=" + schedInstId;
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/QuartzSchedulerThread.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/QuartzSchedulerThread.java
new file mode 100644
index 000000000..6e1f685d5
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/QuartzSchedulerThread.java
@@ -0,0 +1,560 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.JobPersistenceException;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.spi.TriggerFiredBundle;
+
+import java.util.Random;
+
+/**
+ *
+ * The thread responsible for performing the work of firing {@link Trigger}
+ * s that are registered with the {@link QuartzScheduler}
.
+ *
+ *
+ * @see QuartzScheduler
+ * @see com.fr.third.org.quartz.Job
+ * @see Trigger
+ *
+ * @author James House
+ */
+public class QuartzSchedulerThread extends Thread {
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+ private QuartzScheduler qs;
+
+ private QuartzSchedulerResources qsRsrcs;
+
+ private Object sigLock = new Object();
+
+ private boolean signaled;
+ private long signaledNextFireTime;
+
+ private boolean paused;
+
+ private boolean halted;
+
+ private SchedulingContext ctxt = null;
+
+ private Random random = new Random(System.currentTimeMillis());
+
+ // When the scheduler finds there is no current trigger to fire, how long
+ // it should wait until checking again...
+ private static long DEFAULT_IDLE_WAIT_TIME = 30L * 1000L;
+
+ private long idleWaitTime = DEFAULT_IDLE_WAIT_TIME;
+
+ private int idleWaitVariablness = 7 * 1000;
+
+ private long dbFailureRetryInterval = 15L * 1000L;
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Construct a new QuartzSchedulerThread
for the given
+ * QuartzScheduler
as a non-daemon Thread
+ * with normal priority.
+ *
+ */
+ QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs,
+ SchedulingContext ctxt) {
+ this(qs, qsRsrcs, ctxt, qsRsrcs.getMakeSchedulerThreadDaemon(), Thread.NORM_PRIORITY);
+ }
+
+ /**
+ *
+ * Construct a new QuartzSchedulerThread
for the given
+ * QuartzScheduler
as a Thread
with the given
+ * attributes.
+ *
+ */
+ QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs,
+ SchedulingContext ctxt, boolean setDaemon, int threadPrio) {
+ super(qs.getSchedulerThreadGroup(), qsRsrcs.getThreadName());
+ this.qs = qs;
+ this.qsRsrcs = qsRsrcs;
+ this.ctxt = ctxt;
+ this.setDaemon(setDaemon);
+ if(qsRsrcs.isThreadsInheritInitializersClassLoadContext()) {
+ log.info("QuartzSchedulerThread Inheriting ContextClassLoader of thread: " + Thread.currentThread().getName());
+ this.setContextClassLoader(Thread.currentThread().getContextClassLoader());
+ }
+
+ this.setPriority(threadPrio);
+
+ // start the underlying thread, but put this object into the 'paused'
+ // state
+ // so processing doesn't start yet...
+ paused = true;
+ halted = false;
+ this.start();
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ void setIdleWaitTime(long waitTime) {
+ idleWaitTime = waitTime;
+ idleWaitVariablness = (int) (waitTime * 0.2);
+ }
+
+ private long getDbFailureRetryInterval() {
+ return dbFailureRetryInterval;
+ }
+
+ public void setDbFailureRetryInterval(long dbFailureRetryInterval) {
+ this.dbFailureRetryInterval = dbFailureRetryInterval;
+ }
+
+ private long getRandomizedIdleWaitTime() {
+ return idleWaitTime - random.nextInt(idleWaitVariablness);
+ }
+
+ /**
+ *
+ * Signals the main processing loop to pause at the next possible point.
+ *
+ */
+ void togglePause(boolean pause) {
+ synchronized (sigLock) {
+ paused = pause;
+
+ if (paused) {
+ signalSchedulingChange(0);
+ } else {
+ sigLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ *
+ * Signals the main processing loop to pause at the next possible point.
+ *
+ */
+ void halt() {
+ synchronized (sigLock) {
+ halted = true;
+
+ if (paused) {
+ sigLock.notifyAll();
+ } else {
+ signalSchedulingChange(0);
+ }
+ }
+ }
+
+ boolean isPaused() {
+ return paused;
+ }
+
+ /**
+ *
+ * Signals the main processing loop that a change in scheduling has been
+ * made - in order to interrupt any sleeping that may be occuring while
+ * waiting for the fire time to arrive.
+ *
+ *
+ * @param newNextTime the time (in millis) when the newly scheduled trigger
+ * will fire. If this method is being called do to some other even (rather
+ * than scheduling a trigger), the caller should pass zero (0).
+ */
+ public void signalSchedulingChange(long candidateNewNextFireTime) {
+ synchronized(sigLock) {
+ signaled = true;
+ signaledNextFireTime = candidateNewNextFireTime;
+ sigLock.notifyAll();
+ }
+ }
+
+ public void clearSignaledSchedulingChange() {
+ synchronized(sigLock) {
+ signaled = false;
+ signaledNextFireTime = 0;
+ }
+ }
+
+ public boolean isScheduleChanged() {
+ synchronized(sigLock) {
+ return signaled;
+ }
+ }
+
+ public long getSignaledNextFireTime() {
+ synchronized(sigLock) {
+ return signaledNextFireTime;
+ }
+ }
+
+ /**
+ *
+ * The main processing loop of the QuartzSchedulerThread
.
+ *
+ */
+ public void run() {
+ boolean lastAcquireFailed = false;
+
+ while (!halted) {
+ try {
+ // check if we're supposed to pause...
+ synchronized (sigLock) {
+ while (paused && !halted) {
+ try {
+ // wait until togglePause(false) is called...
+ sigLock.wait(1000L);
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ if (halted) {
+ break;
+ }
+ }
+
+ int availTreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
+ if(availTreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads...
+
+ Trigger trigger = null;
+
+ long now = System.currentTimeMillis();
+
+ clearSignaledSchedulingChange();
+ try {
+ trigger = qsRsrcs.getJobStore().acquireNextTrigger(
+ ctxt, now + idleWaitTime);
+ lastAcquireFailed = false;
+ } catch (JobPersistenceException jpe) {
+ if(!lastAcquireFailed) {
+ qs.notifySchedulerListenersError(
+ "An error occured while scanning for the next trigger to fire.",
+ jpe);
+ }
+ lastAcquireFailed = true;
+ } catch (RuntimeException e) {
+ if(!lastAcquireFailed) {
+ getLog().error("quartzSchedulerThreadLoop: RuntimeException "
+ +e.getMessage(), e);
+ }
+ lastAcquireFailed = true;
+ }
+
+ if (trigger != null) {
+
+ now = System.currentTimeMillis();
+ long triggerTime = trigger.getNextFireTime().getTime();
+ long timeUntilTrigger = triggerTime - now;
+ while(timeUntilTrigger > 0) {
+ synchronized(sigLock) {
+ try {
+ // we could have blocked a long while
+ // on 'synchronize', so we must recompute
+ now = System.currentTimeMillis();
+ timeUntilTrigger = triggerTime - now;
+ if(timeUntilTrigger > 1)
+ sigLock.wait(timeUntilTrigger);
+ } catch (InterruptedException ignore) {
+ }
+ }
+ if (isScheduleChanged()) {
+ if(isCandidateNewTimeEarlierWithinReason(triggerTime)) {
+ // above call does a clearSignaledSchedulingChange()
+ try {
+ qsRsrcs.getJobStore().releaseAcquiredTrigger(
+ ctxt, trigger);
+ } catch (JobPersistenceException jpe) {
+ qs.notifySchedulerListenersError(
+ "An error occured while releasing trigger '"
+ + trigger.getFullName() + "'",
+ jpe);
+ // db connection must have failed... keep
+ // retrying until it's up...
+ releaseTriggerRetryLoop(trigger);
+ } catch (RuntimeException e) {
+ getLog().error(
+ "releaseTriggerRetryLoop: RuntimeException "
+ +e.getMessage(), e);
+ // db connection must have failed... keep
+ // retrying until it's up...
+ releaseTriggerRetryLoop(trigger);
+ }
+ trigger = null;
+ break;
+ }
+ }
+ now = System.currentTimeMillis();
+ timeUntilTrigger = triggerTime - now;
+ }
+ if(trigger == null)
+ continue;
+
+ // set trigger to 'executing'
+ TriggerFiredBundle bndle = null;
+
+ boolean goAhead = true;
+ synchronized(sigLock) {
+ goAhead = !halted;
+ }
+ if(goAhead) {
+ try {
+ bndle = qsRsrcs.getJobStore().triggerFired(ctxt,
+ trigger);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError(
+ "An error occured while firing trigger '"
+ + trigger.getFullName() + "'", se);
+ } catch (RuntimeException e) {
+ getLog().error(
+ "RuntimeException while firing trigger " +
+ trigger.getFullName(), e);
+ // db connection must have failed... keep
+ // retrying until it's up...
+ releaseTriggerRetryLoop(trigger);
+ }
+ }
+
+ // it's possible to get 'null' if the trigger was paused,
+ // blocked, or other similar occurrences that prevent it being
+ // fired at this time... or if the scheduler was shutdown (halted)
+ if (bndle == null) {
+ try {
+ qsRsrcs.getJobStore().releaseAcquiredTrigger(ctxt,
+ trigger);
+ } catch (SchedulerException se) {
+ qs.notifySchedulerListenersError(
+ "An error occured while releasing trigger '"
+ + trigger.getFullName() + "'", se);
+ // db connection must have failed... keep retrying
+ // until it's up...
+ releaseTriggerRetryLoop(trigger);
+ }
+ continue;
+ }
+
+ // TODO: improvements:
+ //
+ // 2- make sure we can get a job runshell before firing trigger, or
+ // don't let that throw an exception (right now it never does,
+ // but the signature says it can).
+ // 3- acquire more triggers at a time (based on num threads available?)
+
+
+ JobRunShell shell = null;
+ try {
+ shell = qsRsrcs.getJobRunShellFactory().borrowJobRunShell();
+ shell.initialize(qs, bndle);
+ } catch (SchedulerException se) {
+ try {
+ qsRsrcs.getJobStore().triggeredJobComplete(ctxt,
+ trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR);
+ } catch (SchedulerException se2) {
+ qs.notifySchedulerListenersError(
+ "An error occured while placing job's triggers in error state '"
+ + trigger.getFullName() + "'", se2);
+ // db connection must have failed... keep retrying
+ // until it's up...
+ errorTriggerRetryLoop(bndle);
+ }
+ continue;
+ }
+
+ if (qsRsrcs.getThreadPool().runInThread(shell) == false) {
+ try {
+ // this case should never happen, as it is indicative of the
+ // scheduler being shutdown or a bug in the thread pool or
+ // a thread pool being used concurrently - which the docs
+ // say not to do...
+ getLog().error("ThreadPool.runInThread() return false!");
+ qsRsrcs.getJobStore().triggeredJobComplete(ctxt,
+ trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR);
+ } catch (SchedulerException se2) {
+ qs.notifySchedulerListenersError(
+ "An error occured while placing job's triggers in error state '"
+ + trigger.getFullName() + "'", se2);
+ // db connection must have failed... keep retrying
+ // until it's up...
+ releaseTriggerRetryLoop(trigger);
+ }
+ }
+
+ continue;
+ }
+ } else { // if(availTreadCount > 0)
+ continue; // should never happen, if threadPool.blockForAvailableThreads() follows contract
+ }
+
+ long now = System.currentTimeMillis();
+ long waitTime = now + getRandomizedIdleWaitTime();
+ long timeUntilContinue = waitTime - now;
+ synchronized(sigLock) {
+ try {
+ sigLock.wait(timeUntilContinue);
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ } catch(RuntimeException re) {
+ getLog().error("Runtime error occured in main trigger firing loop.", re);
+ }
+ } // loop...
+
+ // drop references to scheduler stuff to aid garbage collection...
+ qs = null;
+ qsRsrcs = null;
+ }
+
+ private boolean isCandidateNewTimeEarlierWithinReason(long oldTime) {
+
+ // So here's the deal: We know due to being signaled that 'the schedule'
+ // has changed. We may know (if getSignaledNextFireTime() != 0) the
+ // new earliest fire time. We may not (in which case we will assume
+ // that the new time is earlier than the trigger we have acquired).
+ // In either case, we only want to abandon our acquired trigger and
+ // go looking for a new one if "it's worth it". It's only worth it if
+ // the time cost incurred to abandon the trigger and acquire a new one
+ // is less than the time until the currently acquired trigger will fire,
+ // otherwise we're just "thrashing" the job store (e.g. database).
+ //
+ // So the question becomes when is it "worth it"? This will depend on
+ // the job store implementation (and of course the particular database
+ // or whatever behind it). Ideally we would depend on the job store
+ // implementation to tell us the amount of time in which it "thinks"
+ // it can abandon the acquired trigger and acquire a new one. However
+ // we have no current facility for having it tell us that, so we make
+ // a somewhat educated but arbitrary guess ;-).
+
+ synchronized(sigLock) {
+
+ boolean earlier = false;
+
+ if(getSignaledNextFireTime() == 0)
+ earlier = true;
+ else if(getSignaledNextFireTime() < oldTime )
+ earlier = true;
+
+ if(earlier) {
+ // so the new time is considered earlier, but is it enough earlier?
+ // le
+ long diff = oldTime - System.currentTimeMillis();
+ if(diff < (qsRsrcs.getJobStore().supportsPersistence() ? 80L : 7L))
+ earlier = false;
+ }
+
+ clearSignaledSchedulingChange();
+
+ return earlier;
+ }
+ }
+
+ public void errorTriggerRetryLoop(TriggerFiredBundle bndle) {
+ int retryCount = 0;
+ try {
+ while (!halted) {
+ try {
+ Thread.sleep(getDbFailureRetryInterval()); // retry every N
+ // seconds (the db
+ // connection must
+ // be failed)
+ retryCount++;
+ qsRsrcs.getJobStore().triggeredJobComplete(ctxt,
+ bndle.getTrigger(), bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR);
+ retryCount = 0;
+ break;
+ } catch (JobPersistenceException jpe) {
+ if(retryCount % 4 == 0) {
+ qs.notifySchedulerListenersError(
+ "An error occured while releasing trigger '"
+ + bndle.getTrigger().getFullName() + "'", jpe);
+ }
+ } catch (RuntimeException e) {
+ getLog().error("releaseTriggerRetryLoop: RuntimeException "+e.getMessage(), e);
+ } catch (InterruptedException e) {
+ getLog().error("releaseTriggerRetryLoop: InterruptedException "+e.getMessage(), e);
+ }
+ }
+ } finally {
+ if(retryCount == 0) {
+ getLog().info("releaseTriggerRetryLoop: connection restored.");
+ }
+ }
+ }
+
+ public void releaseTriggerRetryLoop(Trigger trigger) {
+ int retryCount = 0;
+ try {
+ while (!halted) {
+ try {
+ Thread.sleep(getDbFailureRetryInterval()); // retry every N
+ // seconds (the db
+ // connection must
+ // be failed)
+ retryCount++;
+ qsRsrcs.getJobStore().releaseAcquiredTrigger(ctxt, trigger);
+ retryCount = 0;
+ break;
+ } catch (JobPersistenceException jpe) {
+ if(retryCount % 4 == 0) {
+ qs.notifySchedulerListenersError(
+ "An error occured while releasing trigger '"
+ + trigger.getFullName() + "'", jpe);
+ }
+ } catch (RuntimeException e) {
+ getLog().error("releaseTriggerRetryLoop: RuntimeException "+e.getMessage(), e);
+ } catch (InterruptedException e) {
+ getLog().error("releaseTriggerRetryLoop: InterruptedException "+e.getMessage(), e);
+ }
+ }
+ } finally {
+ if(retryCount == 0) {
+ getLog().info("releaseTriggerRetryLoop: connection restored.");
+ }
+ }
+ }
+
+ public Log getLog() {
+ return log;
+ }
+
+} // end of QuartzSchedulerThread
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/RemotableQuartzScheduler.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/RemotableQuartzScheduler.java
new file mode 100644
index 000000000..fe298bf24
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/RemotableQuartzScheduler.java
@@ -0,0 +1,235 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.core;
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import com.fr.third.org.quartz.Calendar;
+import com.fr.third.org.quartz.JobDataMap;
+import com.fr.third.org.quartz.JobDetail;
+import com.fr.third.org.quartz.JobListener;
+import com.fr.third.org.quartz.SchedulerContext;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.SchedulerListener;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.TriggerListener;
+import com.fr.third.org.quartz.UnableToInterruptJobException;
+
+/**
+ * @author James House
+ */
+public interface RemotableQuartzScheduler extends Remote {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ String getSchedulerName() throws RemoteException;
+
+ String getSchedulerInstanceId() throws RemoteException;
+
+ SchedulerContext getSchedulerContext() throws SchedulerException,
+ RemoteException;
+
+ void start() throws SchedulerException, RemoteException;
+
+ void startDelayed(int seconds) throws SchedulerException, RemoteException;
+
+ void standby() throws RemoteException;
+
+ boolean isInStandbyMode() throws RemoteException;
+
+ void shutdown() throws RemoteException;
+
+ void shutdown(boolean waitForJobsToComplete) throws RemoteException;
+
+ boolean isShutdown() throws RemoteException;
+
+ Date runningSince() throws RemoteException;
+
+ String getVersion() throws RemoteException;
+
+ int numJobsExecuted() throws RemoteException;
+
+ Class getJobStoreClass() throws RemoteException;
+
+ boolean supportsPersistence() throws RemoteException;
+
+ Class getThreadPoolClass() throws RemoteException;
+
+ int getThreadPoolSize() throws RemoteException;
+
+ List getCurrentlyExecutingJobs() throws SchedulerException,
+ RemoteException;
+
+ Date scheduleJob(SchedulingContext ctxt, JobDetail jobDetail,
+ Trigger trigger) throws SchedulerException, RemoteException;
+
+ Date scheduleJob(SchedulingContext ctxt, Trigger trigger)
+ throws SchedulerException, RemoteException;
+
+ void addJob(SchedulingContext ctxt, JobDetail jobDetail,
+ boolean replace) throws SchedulerException, RemoteException;
+
+ boolean deleteJob(SchedulingContext ctxt, String jobName,
+ String groupName) throws SchedulerException, RemoteException;
+
+ boolean unscheduleJob(SchedulingContext ctxt, String triggerName,
+ String groupName) throws SchedulerException, RemoteException;
+
+ Date rescheduleJob(SchedulingContext ctxt, String triggerName,
+ String groupName, Trigger newTrigger) throws SchedulerException, RemoteException;
+
+
+ void triggerJob(SchedulingContext ctxt, String jobName,
+ String groupName, JobDataMap data) throws SchedulerException, RemoteException;
+
+ void triggerJobWithVolatileTrigger(SchedulingContext ctxt,
+ String jobName, String groupName, JobDataMap data) throws SchedulerException,
+ RemoteException;
+
+ void pauseTrigger(SchedulingContext ctxt, String triggerName,
+ String groupName) throws SchedulerException, RemoteException;
+
+ void pauseTriggerGroup(SchedulingContext ctxt, String groupName)
+ throws SchedulerException, RemoteException;
+
+ void pauseJob(SchedulingContext ctxt, String jobName,
+ String groupName) throws SchedulerException, RemoteException;
+
+ void pauseJobGroup(SchedulingContext ctxt, String groupName)
+ throws SchedulerException, RemoteException;
+
+ void resumeTrigger(SchedulingContext ctxt, String triggerName,
+ String groupName) throws SchedulerException, RemoteException;
+
+ void resumeTriggerGroup(SchedulingContext ctxt, String groupName)
+ throws SchedulerException, RemoteException;
+
+ Set getPausedTriggerGroups(SchedulingContext ctxt)
+ throws SchedulerException, RemoteException;
+
+ void resumeJob(SchedulingContext ctxt, String jobName,
+ String groupName) throws SchedulerException, RemoteException;
+
+ void resumeJobGroup(SchedulingContext ctxt, String groupName)
+ throws SchedulerException, RemoteException;
+
+ void pauseAll(SchedulingContext ctxt) throws SchedulerException,
+ RemoteException;
+
+ void resumeAll(SchedulingContext ctxt) throws SchedulerException,
+ RemoteException;
+
+ String[] getJobGroupNames(SchedulingContext ctxt)
+ throws SchedulerException, RemoteException;
+
+ String[] getJobNames(SchedulingContext ctxt, String groupName)
+ throws SchedulerException, RemoteException;
+
+ Trigger[] getTriggersOfJob(SchedulingContext ctxt, String jobName,
+ String groupName) throws SchedulerException, RemoteException;
+
+ String[] getTriggerGroupNames(SchedulingContext ctxt)
+ throws SchedulerException, RemoteException;
+
+ String[] getTriggerNames(SchedulingContext ctxt, String groupName)
+ throws SchedulerException, RemoteException;
+
+ JobDetail getJobDetail(SchedulingContext ctxt, String jobName,
+ String jobGroup) throws SchedulerException, RemoteException;
+
+ Trigger getTrigger(SchedulingContext ctxt, String triggerName,
+ String triggerGroup) throws SchedulerException, RemoteException;
+
+ int getTriggerState(SchedulingContext ctxt, String triggerName,
+ String triggerGroup) throws SchedulerException, RemoteException;
+
+ void addCalendar(SchedulingContext ctxt, String calName,
+ Calendar calendar, boolean replace, boolean updateTriggers) throws SchedulerException,
+ RemoteException;
+
+ boolean deleteCalendar(SchedulingContext ctxt, String calName)
+ throws SchedulerException, RemoteException;
+
+ Calendar getCalendar(SchedulingContext ctxt, String calName)
+ throws SchedulerException, RemoteException;
+
+ String[] getCalendarNames(SchedulingContext ctxt)
+ throws SchedulerException, RemoteException;
+
+ void addGlobalJobListener(JobListener jobListener)
+ throws RemoteException;
+
+ void addJobListener(JobListener jobListener) throws RemoteException;
+
+ boolean removeGlobalJobListener(String name) throws RemoteException;
+
+ boolean removeJobListener(String name) throws RemoteException;
+
+ List getGlobalJobListeners() throws RemoteException;
+
+ Set getJobListenerNames() throws RemoteException;
+
+ JobListener getGlobalJobListener(String name) throws RemoteException;
+
+ JobListener getJobListener(String name) throws RemoteException;
+
+ void addGlobalTriggerListener(TriggerListener triggerListener)
+ throws RemoteException;
+
+ void addTriggerListener(TriggerListener triggerListener)
+ throws RemoteException;
+
+ boolean removeGlobalTriggerListener(String name)
+ throws RemoteException;
+
+ boolean removeTriggerListener(String name) throws RemoteException;
+
+ List getGlobalTriggerListeners() throws RemoteException;
+
+ Set getTriggerListenerNames() throws RemoteException;
+
+ TriggerListener getGlobalTriggerListener(String name)
+ throws RemoteException;
+
+ TriggerListener getTriggerListener(String name)
+ throws RemoteException;
+
+ void addSchedulerListener(SchedulerListener schedulerListener)
+ throws RemoteException;
+
+ boolean removeSchedulerListener(SchedulerListener schedulerListener)
+ throws RemoteException;
+
+ List getSchedulerListeners() throws RemoteException;
+
+ boolean interrupt(SchedulingContext ctxt, String jobName, String groupName) throws UnableToInterruptJobException,RemoteException ;
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/SchedulerSignalerImpl.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/SchedulerSignalerImpl.java
new file mode 100644
index 000000000..51003eeee
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/SchedulerSignalerImpl.java
@@ -0,0 +1,93 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.spi.SchedulerSignaler;
+
+/**
+ * An interface to be used by JobStore
instances in order to
+ * communicate signals back to the QuartzScheduler
.
+ *
+ * @author jhouse
+ */
+public class SchedulerSignalerImpl implements SchedulerSignaler {
+
+ Log log = LogFactory.getLog(SchedulerSignalerImpl.class);
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ protected QuartzScheduler sched;
+ protected QuartzSchedulerThread schedThread;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public SchedulerSignalerImpl(QuartzScheduler sched, QuartzSchedulerThread schedThread) {
+ this.sched = sched;
+ this.schedThread = schedThread;
+
+ log.info("Initialized Scheduler Signaller of type: " + getClass());
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public void notifyTriggerListenersMisfired(Trigger trigger) {
+ try {
+ sched.notifyTriggerListenersMisfired(trigger);
+ } catch (SchedulerException se) {
+ sched.getLog().error(
+ "Error notifying listeners of trigger misfire.", se);
+ sched.notifySchedulerListenersError(
+ "Error notifying listeners of trigger misfire.", se);
+ }
+ }
+
+ public void notifySchedulerListenersFinalized(Trigger trigger) {
+ sched.notifySchedulerListenersFinalized(trigger);
+ }
+
+ public void signalSchedulingChange(long candidateNewNextFireTime) {
+ schedThread.signalSchedulingChange(candidateNewNextFireTime);
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/SchedulingContext.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/SchedulingContext.java
new file mode 100644
index 000000000..58f8f8e03
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/SchedulingContext.java
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.core;
+
+/**
+ *
+ * An object used to pass information about the 'client' to the QuartzScheduler
.
+ *
+ *
+ * @see QuartzScheduler
+ *
+ * @author James House
+ */
+public class SchedulingContext implements java.io.Serializable {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private String instanceId;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Construct a SchedulingContext with default values.
+ *
+ */
+ public SchedulingContext() {
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * get the instanceId in the cluster.
+ *
+ */
+ public String getInstanceId() {
+ return this.instanceId;
+ }
+
+ /**
+ *
+ * Set the instanceId.
+ *
+ */
+ public void setInstanceId(String instanceId) {
+ this.instanceId = instanceId;
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/mbeans-descriptors.xml b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/mbeans-descriptors.xml
new file mode 100644
index 000000000..7a833728c
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/mbeans-descriptors.xml
@@ -0,0 +1,234 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/package.html b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/package.html
new file mode 100644
index 000000000..3f14da662
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/core/package.html
@@ -0,0 +1,15 @@
+
+
+Package com.fr.third.org.quartz.core
+
+
+Contains the core classes and interfaces for the Quartz job scheduler.
+
+
+
+
+See the Quartz project
+ at Open Symphony for more information.
+
+
+
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ee/jta/JTAJobRunShell.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ee/jta/JTAJobRunShell.java
new file mode 100644
index 000000000..d2c8965ee
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ee/jta/JTAJobRunShell.java
@@ -0,0 +1,166 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.ee.jta;
+
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.core.JobRunShell;
+import com.fr.third.org.quartz.core.JobRunShellFactory;
+import com.fr.third.org.quartz.core.SchedulingContext;
+
+/**
+ *
+ * An extension of {@link com.fr.third.org.quartz.core.JobRunShell}
that
+ * begins an XA transaction before executing the Job, and commits (or
+ * rolls-back) the transaction after execution completes.
+ *
+ *
+ * @see com.fr.third.org.quartz.core.JobRunShell
+ *
+ * @author James House
+ */
+public class JTAJobRunShell extends JobRunShell {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private UserTransaction ut;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Create a JTAJobRunShell instance with the given settings.
+ *
+ */
+ public JTAJobRunShell(JobRunShellFactory jobRunShellFactory,
+ Scheduler scheduler, SchedulingContext schdCtxt) {
+ super(jobRunShellFactory, scheduler, schdCtxt);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ protected void begin() throws SchedulerException {
+ // Don't get a new UserTransaction w/o making sure we cleaned up the old
+ // one. This is necessary because there are paths through JobRunShell.run()
+ // where begin() can be called multiple times w/o complete being called in
+ // between.
+ cleanupUserTransaction();
+
+ boolean beganSuccessfully = false;
+ try {
+ getLog().debug("Looking up UserTransaction.");
+ ut = UserTransactionHelper.lookupUserTransaction();
+
+ getLog().debug("Beginning UserTransaction.");
+ ut.begin();
+
+ beganSuccessfully = true;
+ } catch (SchedulerException se) {
+ throw se;
+ } catch (Exception nse) {
+
+ throw new SchedulerException(
+ "JTAJobRunShell could not start UserTransaction.", nse);
+ } finally {
+ if (beganSuccessfully == false) {
+ cleanupUserTransaction();
+ }
+ }
+ }
+
+ protected void complete(boolean successfulExecution)
+ throws SchedulerException {
+ if (ut == null) {
+ return;
+ }
+
+ try {
+ try {
+ if (ut.getStatus() == Status.STATUS_MARKED_ROLLBACK) {
+ getLog().debug("UserTransaction marked for rollback only.");
+ successfulExecution = false;
+ }
+ } catch (SystemException e) {
+ throw new SchedulerException(
+ "JTAJobRunShell could not read UserTransaction status.", e);
+ }
+
+ if (successfulExecution) {
+ try {
+ getLog().debug("Committing UserTransaction.");
+ ut.commit();
+ } catch (Exception nse) {
+ throw new SchedulerException(
+ "JTAJobRunShell could not commit UserTransaction.", nse);
+ }
+ } else {
+ try {
+ getLog().debug("Rolling-back UserTransaction.");
+ ut.rollback();
+ } catch (Exception nse) {
+ throw new SchedulerException(
+ "JTAJobRunShell could not rollback UserTransaction.",
+ nse);
+ }
+ }
+ } finally {
+ cleanupUserTransaction();
+ }
+ }
+
+ /**
+ * Override passivate() to ensure we always cleanup the UserTransaction.
+ */
+ public void passivate() {
+ cleanupUserTransaction();
+ super.passivate();
+ }
+
+ private void cleanupUserTransaction() {
+ if (ut != null) {
+ UserTransactionHelper.returnUserTransaction(ut);
+ ut = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ee/jta/JTAJobRunShellFactory.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ee/jta/JTAJobRunShellFactory.java
new file mode 100644
index 000000000..bc73b89e4
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ee/jta/JTAJobRunShellFactory.java
@@ -0,0 +1,114 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.ee.jta;
+
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerConfigException;
+import com.fr.third.org.quartz.core.JobRunShell;
+import com.fr.third.org.quartz.core.JobRunShellFactory;
+import com.fr.third.org.quartz.core.SchedulingContext;
+
+/**
+ *
+ * Responsible for creating the instances of {@link com.fr.third.org.quartz.ee.jta.JTAJobRunShell}
+ * to be used within the {@link com.fr.third.org.quartz.core.QuartzScheduler}
+ *
instance.
+ *
+ *
+ *
+ * This implementation does not re-use any objects, it simply makes a new
+ * JTAJobRunShell each time borrowJobRunShell()
is called.
+ *
+ *
+ * @author James House
+ */
+public class JTAJobRunShellFactory implements JobRunShellFactory {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private Scheduler scheduler;
+
+ private SchedulingContext schedCtxt;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public JTAJobRunShellFactory() {
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Initialize the factory, providing a handle to the Scheduler
+ * that should be made available within the JobRunShell
and
+ * the JobExecutionContext
s within it, and a handle to the
+ * SchedulingContext
that the shell will use in its own
+ * operations with the JobStore
.
+ *
+ */
+ public void initialize(Scheduler scheduler, SchedulingContext schedCtxt)
+ throws SchedulerConfigException {
+ this.scheduler = scheduler;
+ this.schedCtxt = schedCtxt;
+ }
+
+ /**
+ *
+ * Called by the {@link com.fr.third.org.quartz.core.QuartzSchedulerThread}
+ *
to obtain instances of
+ * {@link com.fr.third.org.quartz.core.JobRunShell}
.
+ *
+ */
+ public JobRunShell borrowJobRunShell() {
+ return new JTAJobRunShell(this, scheduler, schedCtxt);
+ }
+
+ /**
+ *
+ * Called by the {@link com.fr.third.org.quartz.core.QuartzSchedulerThread}
+ *
to return instances of
+ * {@link com.fr.third.org.quartz.core.JobRunShell}
.
+ *
+ */
+ public void returnJobRunShell(JobRunShell jobRunShell) {
+ jobRunShell.passivate();
+ }
+
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ee/jta/UserTransactionHelper.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ee/jta/UserTransactionHelper.java
new file mode 100644
index 000000000..bc72aa5c1
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/ee/jta/UserTransactionHelper.java
@@ -0,0 +1,225 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.ee.jta;
+
+import javax.naming.InitialContext;
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.SystemException;
+import javax.transaction.TransactionManager;
+import javax.transaction.UserTransaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.SchedulerException;
+
+/**
+ *
+ * A helper for obtaining a handle to a UserTransaction...
+ *
+ *
+ * To ensure proper cleanup of the InitalContext used to create/lookup
+ * the UserTransaction, be sure to always call returnUserTransaction() when
+ * you are done with the UserTransaction.
+ *
+ *
+ * @author James House
+ */
+public class UserTransactionHelper {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constants.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public static final String DEFAULT_USER_TX_LOCATION = "java:comp/UserTransaction";
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private static String userTxURL = DEFAULT_USER_TX_LOCATION;
+
+ private static Log log = LogFactory.getLog(UserTransactionHelper.class);
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Do not allow the creation of an all static utility class.
+ */
+ private UserTransactionHelper() {
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public static String getUserTxLocation() {
+ return userTxURL;
+ }
+
+ /**
+ * Set the JNDI URL at which the Application Server's UserTransaction can
+ * be found. If not set, the default value is "java:comp/UserTransaction" -
+ * which works for nearly all application servers.
+ */
+ public static void setUserTxLocation(String userTxURL) {
+ if (userTxURL != null) {
+ UserTransactionHelper.userTxURL = userTxURL;
+ }
+ }
+
+ /**
+ * Create/Lookup a UserTransaction in the InitialContext via the
+ * name set in setUserTxLocation().
+ */
+ public static UserTransaction lookupUserTransaction() throws SchedulerException {
+ return new UserTransactionWithContext();
+ }
+
+ /**
+ * Return a UserTransaction that was retrieved via getUserTransaction().
+ * This will make sure that the InitalContext used to lookup/create the
+ * UserTransaction is properly cleaned up.
+ */
+ public static void returnUserTransaction(UserTransaction userTransaction) {
+ if ((userTransaction != null) &&
+ (userTransaction instanceof UserTransactionWithContext)) {
+ UserTransactionWithContext userTransactionWithContext =
+ (UserTransactionWithContext)userTransaction;
+
+ userTransactionWithContext.closeContext();
+ }
+ }
+
+
+ /**
+ * This class wraps a UserTransaction with the InitialContext that was used
+ * to look it up, so that when the UserTransaction is returned to the
+ * UserTransactionHelper the InitialContext can be closed.
+ */
+ private static class UserTransactionWithContext implements UserTransaction {
+ InitialContext context;
+ UserTransaction userTransaction;
+
+ public UserTransactionWithContext() throws SchedulerException {
+ try {
+ context = new InitialContext();
+ } catch (Throwable t) {
+ throw new SchedulerException(
+ "UserTransactionHelper failed to create InitialContext to lookup/create UserTransaction.", t);
+ }
+
+ try {
+ userTransaction = (UserTransaction)context.lookup(userTxURL);
+ } catch (Throwable t) {
+ closeContext();
+ throw new SchedulerException(
+ "UserTransactionHelper could not lookup/create UserTransaction.",
+ t);
+ }
+
+ if (userTransaction == null) {
+ closeContext();
+ throw new SchedulerException(
+ "UserTransactionHelper could not lookup/create UserTransaction from the InitialContext.");
+ }
+ }
+
+ /**
+ * Close the InitialContext that was used to lookup/create the
+ * underlying UserTransaction.
+ */
+ public void closeContext() {
+ try {
+ if (context != null) {
+ context.close();
+ }
+ } catch (Throwable t) {
+ getLog().warn("Failed to close InitialContext used to get a UserTransaction.", t);
+ }
+ context = null;
+ }
+
+ /**
+ * When we are being garbage collected, make sure we were properly
+ * returned to the UserTransactionHelper.
+ */
+ protected void finalize() throws Throwable {
+ try {
+ if (context != null) {
+ getLog().warn("UserTransaction was never returned to the UserTransactionHelper.");
+ closeContext();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private static Log getLog() {
+ return LogFactory.getLog(UserTransactionWithContext.class);
+ }
+
+ // Wrapper methods that just delegate to the underlying UserTransaction
+
+ public void begin() throws NotSupportedException, SystemException {
+ userTransaction.begin();
+ }
+
+ public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException {
+ userTransaction.commit();
+ }
+
+ public void rollback() throws IllegalStateException, SecurityException, SystemException {
+ userTransaction.rollback();
+ }
+
+ public void setRollbackOnly() throws IllegalStateException, SystemException {
+ userTransaction.setRollbackOnly();
+ }
+
+ public int getStatus() throws SystemException {
+ return userTransaction.getStatus();
+ }
+
+ public void setTransactionTimeout(int seconds) throws SystemException {
+ userTransaction.setTransactionTimeout(seconds);
+ }
+ }
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/helpers/TriggerUtils.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/helpers/TriggerUtils.java
new file mode 100644
index 000000000..7f50a2d94
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/helpers/TriggerUtils.java
@@ -0,0 +1,1194 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.helpers;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TimeZone;
+
+import com.fr.third.org.quartz.CronTrigger;
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SimpleTrigger;
+import com.fr.third.org.quartz.Trigger;
+
+/**
+ *
+ * Convenience and utility methods for simplifying the construction and
+ * configuration of {@link Trigger}s
.
+ *
+ *
+ *
+ * Please submit suggestions for additional convenience methods to either the
+ * Quartz user forum or the developer's mail list at
+ * source forge.
+ *
+ *
+ * @see CronTrigger
+ * @see SimpleTrigger
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ * @author James House
+ */
+public class TriggerUtils {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constants.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public static final int SUNDAY = 1;
+
+ public static final int MONDAY = 2;
+
+ public static final int TUESDAY = 3;
+
+ public static final int WEDNESDAY = 4;
+
+ public static final int THURSDAY = 5;
+
+ public static final int FRIDAY = 6;
+
+ public static final int SATURDAY = 7;
+
+ public static final int LAST_DAY_OF_MONTH = -1;
+
+ public static final long MILLISECONDS_IN_MINUTE = 60l * 1000l;
+
+ public static final long MILLISECONDS_IN_HOUR = 60l * 60l * 1000l;
+
+ public static final long SECONDS_IN_DAY = 24l * 60l * 60L;
+
+ public static final long MILLISECONDS_IN_DAY = SECONDS_IN_DAY * 1000l;
+
+ /**
+ * Private constructor because this is a pure utility class.
+ */
+ private TriggerUtils() {
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private static void validateDayOfWeek(int dayOfWeek) {
+ if (dayOfWeek < SUNDAY || dayOfWeek > SATURDAY) {
+ throw new IllegalArgumentException("Invalid day of week.");
+ }
+ }
+
+ private static void validateHour(int hour) {
+ if (hour < 0 || hour > 23) {
+ throw new IllegalArgumentException(
+ "Invalid hour (must be >= 0 and <= 23).");
+ }
+ }
+
+ private static void validateMinute(int minute) {
+ if (minute < 0 || minute > 59) {
+ throw new IllegalArgumentException(
+ "Invalid minute (must be >= 0 and <= 59).");
+ }
+ }
+
+ private static void validateSecond(int second) {
+ if (second < 0 || second > 59) {
+ throw new IllegalArgumentException(
+ "Invalid second (must be >= 0 and <= 59).");
+ }
+ }
+
+ private static void validateDayOfMonth(int day) {
+ if ((day < 1 || day > 31) && day != LAST_DAY_OF_MONTH) {
+ throw new IllegalArgumentException("Invalid day of month.");
+ }
+ }
+
+ private static void validateMonth(int month) {
+ if (month < 1 || month > 12) {
+ throw new IllegalArgumentException(
+ "Invalid month (must be >= 1 and <= 12.");
+ }
+ }
+
+ private static void validateYear(int year) {
+ if (year < 1970 || year > 2099) {
+ throw new IllegalArgumentException(
+ "Invalid year (must be >= 1970 and <= 2099.");
+ }
+ }
+
+ /**
+ *
+ * Set the given Trigger
's name to the given value, and its
+ * group to the default group (Scheduler.DEFAULT_GROUP
).
+ *
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static void setTriggerIdentity(Trigger trig, String name) {
+ trig.setName(name);
+ trig.setGroup(Scheduler.DEFAULT_GROUP);
+ }
+
+ /**
+ *
+ * Set the given Trigger
's name to the given value, and its
+ * group to the given group.
+ *
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static void setTriggerIdentity(Trigger trig, String name,
+ String group) {
+ trig.setName(name);
+ trig.setGroup(group);
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every day at the given time.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ * @param hour
+ * the hour (0-23) upon which to fire
+ * @param minute
+ * the minute (0-59) upon which to fire
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeDailyTrigger(int hour, int minute) {
+ validateHour(hour);
+ validateMinute(minute);
+
+ CronTrigger trig = new CronTrigger();
+
+ try {
+ trig.setCronExpression("0 " + minute + " " + hour + " ? * *");
+ } catch (Exception ignore) {
+ return null; /* never happens... */
+ }
+
+ return trig;
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every day at the given time.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ * @param dayOfWeek
+ * (1-7) the day of week upon which to fire
+ * @param hour
+ * the hour (0-23) upon which to fire
+ * @param minute
+ * the minute (0-59) upon which to fire
+ *
+ * @see #SUNDAY
+ * @see #MONDAY
+ * @see #TUESDAY
+ * @see #WEDNESDAY
+ * @see #THURSDAY
+ * @see #FRIDAY
+ * @see #SATURDAY
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeWeeklyTrigger(int dayOfWeek, int hour, int minute) {
+ validateDayOfWeek(dayOfWeek);
+ validateHour(hour);
+ validateMinute(minute);
+
+ CronTrigger trig = new CronTrigger();
+
+ try {
+ trig.setCronExpression("0 " + minute + " " + hour + " ? * "
+ + dayOfWeek);
+ } catch (Exception ignore) {
+ return null; /* never happens... */
+ }
+
+ return trig;
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every day at the given time.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ *
+ * If the day of the month specified does not occur in a given month, a
+ * firing will not occur that month. (i.e. if dayOfMonth is specified as
+ * 31, no firing will occur in the months of the year with fewer than 31
+ * days).
+ *
+ *
+ * @param dayOfMonth
+ * (1-31, or -1) the day of week upon which to fire
+ * @param hour
+ * the hour (0-23) upon which to fire
+ * @param minute
+ * the minute (0-59) upon which to fire
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeMonthlyTrigger(int dayOfMonth, int hour,
+ int minute) {
+ validateDayOfMonth(dayOfMonth);
+ validateHour(hour);
+ validateMinute(minute);
+
+ CronTrigger trig = new CronTrigger();
+
+ try {
+ if (dayOfMonth != LAST_DAY_OF_MONTH) {
+ trig.setCronExpression("0 " + minute + " " + hour + " " + dayOfMonth + " * ?");
+ } else {
+ trig.setCronExpression("0 " + minute + " " + hour + " L * ?");
+ }
+ } catch (Exception ignore) {
+ return null; /* never happens... */
+ }
+
+ return trig;
+ }
+
+ /*
+ * Make a trigger that will fire every N days at the given time.
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ * @param hour the hour (0-23) upon which to fire @param minute the minute
+ * (0-59) upon which to fire @param interval the number of days between
+ * firings public static Trigger makeDailyTrigger(int interval, int hour,
+ * int minute) {
+ *
+ * SimpleTrigger trig = new SimpleTrigger();
+ *
+ * MILLISECONDS_IN_DAY);
+ * trig.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
+ *
+ * return trig;
+ * }
+ */
+
+ /**
+ *
+ * Make a trigger that will fire every second, indefinitely.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeSecondlyTrigger() {
+ return makeSecondlyTrigger(1, SimpleTrigger.REPEAT_INDEFINITELY);
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every N seconds, indefinitely.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ * @param intervalInSeconds
+ * the number of seconds between firings
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeSecondlyTrigger(int intervalInSeconds) {
+ return makeSecondlyTrigger(intervalInSeconds,
+ SimpleTrigger.REPEAT_INDEFINITELY);
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every N seconds, with the given number of
+ * repeats.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ * @param intervalInSeconds
+ * the number of seconds between firings
+ * @param repeatCount
+ * the number of times to repeat the firing
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeSecondlyTrigger(int intervalInSeconds,
+ int repeatCount) {
+ SimpleTrigger trig = new SimpleTrigger();
+
+ trig.setRepeatInterval(intervalInSeconds * 1000l);
+ trig.setRepeatCount(repeatCount);
+
+ return trig;
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every minute, indefinitely.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeMinutelyTrigger() {
+ return makeMinutelyTrigger(1, SimpleTrigger.REPEAT_INDEFINITELY);
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every N minutes, indefinitely.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ * @param intervalInMinutes
+ * the number of minutes between firings
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeMinutelyTrigger(int intervalInMinutes) {
+ return makeMinutelyTrigger(intervalInMinutes,
+ SimpleTrigger.REPEAT_INDEFINITELY);
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every N minutes, with the given number of
+ * repeats.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ * @param intervalInMinutes
+ * the number of minutes between firings
+ * @param repeatCount
+ * the number of times to repeat the firing
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeMinutelyTrigger(int intervalInMinutes,
+ int repeatCount) {
+ SimpleTrigger trig = new SimpleTrigger();
+
+ trig.setRepeatInterval(intervalInMinutes * MILLISECONDS_IN_MINUTE);
+ trig.setRepeatCount(repeatCount);
+
+ return trig;
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every hour, indefinitely.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeHourlyTrigger() {
+ return makeHourlyTrigger(1, SimpleTrigger.REPEAT_INDEFINITELY);
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every N hours, indefinitely.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ * @param intervalInHours
+ * the number of hours between firings
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeHourlyTrigger(int intervalInHours) {
+ return makeHourlyTrigger(intervalInHours,
+ SimpleTrigger.REPEAT_INDEFINITELY);
+ }
+
+ /**
+ *
+ * Make a trigger that will fire every N hours, with the given number of
+ * repeats.
+ *
+ *
+ *
+ * The generated trigger will still need to have its name, group,
+ * start-time and end-time set.
+ *
+ *
+ * @param intervalInHours
+ * the number of hours between firings
+ * @param repeatCount
+ * the number of times to repeat the firing
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Trigger makeHourlyTrigger(int intervalInHours, int repeatCount) {
+ SimpleTrigger trig = new SimpleTrigger();
+
+ trig.setRepeatInterval(intervalInHours * MILLISECONDS_IN_HOUR);
+ trig.setRepeatCount(repeatCount);
+
+ return trig;
+ }
+
+ /**
+ *
+ * Returns a date that is rounded to the next even hour above the given
+ * date.
+ *
+ *
+ *
+ * For example an input date with a time of 08:13:54 would result in a date
+ * with the time of 09:00:00. If the date's time is in the 23rd hour, the
+ * date's 'day' will be promoted, and the time will be set to 00:00:00.
+ *
+ *
+ * @param date
+ * the Date to round, if null
the current time will
+ * be used
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getEvenHourDate(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) + 1);
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ *
+ * Returns a date that is rounded to the previous even hour below the given
+ * date.
+ *
+ *
+ *
+ * For example an input date with a time of 08:13:54 would result in a date
+ * with the time of 08:00:00.
+ *
+ *
+ * @param date
+ * the Date to round, if null
the current time will
+ * be used
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getEvenHourDateBefore(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ *
+ * Returns a date that is rounded to the next even hour above the given
+ * date.
+ *
+ *
+ *
+ * For example an input date with a time of 08:13:54 would result in a date
+ * with the time of 08:14:00. If the date's time is in the 59th minute,
+ * then the hour (and possibly the day) will be promoted.
+ *
+ *
+ * @param date
+ * the Date to round, if null
the current time will
+ * be used
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getEvenMinuteDate(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + 1);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ *
+ * Returns a date that is rounded to the previous even hour below the given
+ * date.
+ *
+ *
+ *
+ * For example an input date with a time of 08:13:54 would result in a date
+ * with the time of 08:13:00.
+ *
+ *
+ * @param date
+ * the Date to round, if null
the current time will
+ * be used
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getEvenMinuteDateBefore(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ *
+ * Returns a date that is rounded to the next even second above the given
+ * date.
+ *
+ *
+ * @param date
+ * the Date to round, if null
the current time will
+ * be used
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getEvenSecondDate(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ c.set(Calendar.SECOND, c.get(Calendar.SECOND) + 1);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ *
+ * Returns a date that is rounded to the previous even second below the
+ * given date.
+ *
+ *
+ *
+ * For example an input date with a time of 08:13:54.341 would result in a
+ * date with the time of 08:13:00.000.
+ *
+ *
+ * @param date
+ * the Date to round, if null
the current time will
+ * be used
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getEvenSecondDateBefore(Date date) {
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ *
+ * Returns a date that is rounded to the next even multiple of the given
+ * minute.
+ *
+ *
+ *
+ * For example an input date with a time of 08:13:54, and an input
+ * minute-base of 5 would result in a date with the time of 08:15:00. The
+ * same input date with an input minute-base of 10 would result in a date
+ * with the time of 08:20:00. But a date with the time 08:53:31 and an
+ * input minute-base of 45 would result in 09:00:00, because the even-hour
+ * is the next 'base' for 45-minute intervals.
+ *
+ *
+ *
+ * More examples:
+ *
+ * Input Time
+ * Minute-Base
+ * Result Time
+ *
+ *
+ * 11:16:41
+ * 20
+ * 11:20:00
+ *
+ *
+ * 11:36:41
+ * 20
+ * 11:40:00
+ *
+ *
+ * 11:46:41
+ * 20
+ * 12:00:00
+ *
+ *
+ * 11:26:41
+ * 30
+ * 11:30:00
+ *
+ *
+ * 11:36:41
+ * 30
+ * 12:00:00
+ *
+ * 11:16:41
+ * 17
+ * 11:17:00
+ *
+ *
+ * 11:17:41
+ * 17
+ * 11:34:00
+ *
+ *
+ * 11:52:41
+ * 17
+ * 12:00:00
+ *
+ *
+ * 11:52:41
+ * 5
+ * 11:55:00
+ *
+ *
+ * 11:57:41
+ * 5
+ * 12:00:00
+ *
+ *
+ * 11:17:41
+ * 0
+ * 12:00:00
+ *
+ *
+ * 11:17:41
+ * 1
+ * 11:08:00
+ *
+ *
+ *
+ *
+ * @param date
+ * the Date to round, if null
the current time will
+ * be used
+ * @param minuteBase
+ * the base-minute to set the time on
+ *
+ * @see #getNextGivenSecondDate(Date, int)
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getNextGivenMinuteDate(Date date, int minuteBase) {
+ if (minuteBase < 0 || minuteBase > 59) {
+ throw new IllegalArgumentException(
+ "minuteBase must be >=0 and <= 59");
+ }
+
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ if (minuteBase == 0) {
+ c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) + 1);
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ int minute = c.get(Calendar.MINUTE);
+
+ int arItr = minute / minuteBase;
+
+ int nextMinuteOccurance = minuteBase * (arItr + 1);
+
+ if (nextMinuteOccurance < 60) {
+ c.set(Calendar.MINUTE, nextMinuteOccurance);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ } else {
+ c.set(Calendar.HOUR_OF_DAY, c.get(Calendar.HOUR_OF_DAY) + 1);
+ c.set(Calendar.MINUTE, 0);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+ }
+
+ /**
+ *
+ * Returns a date that is rounded to the next even multiple of the given
+ * minute.
+ *
+ *
+ *
+ * The rules for calculating the second are the same as those for
+ * calculating the minute in the method getNextGivenMinuteDate(..).
+ * *
+ * @param date the Date to round, if null
the current time will
+ * be used
+ * @param secondBase the base-second to set the time on
+ *
+ * @see #getNextGivenMinuteDate(Date, int)
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getNextGivenSecondDate(Date date, int secondBase) {
+ if (secondBase < 0 || secondBase > 59) {
+ throw new IllegalArgumentException(
+ "secondBase must be >=0 and <= 59");
+ }
+
+ if (date == null) {
+ date = new Date();
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ if (secondBase == 0) {
+ c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + 1);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ int second = c.get(Calendar.SECOND);
+
+ int arItr = second / secondBase;
+
+ int nextSecondOccurance = secondBase * (arItr + 1);
+
+ if (nextSecondOccurance < 60) {
+ c.set(Calendar.SECOND, nextSecondOccurance);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ } else {
+ c.set(Calendar.MINUTE, c.get(Calendar.MINUTE) + 1);
+ c.set(Calendar.SECOND, 0);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+ }
+
+ /**
+ *
+ * Get a Date
object that represents the given time, on
+ * today's date.
+ *
+ *
+ * @param second
+ * The value (0-59) to give the seconds field of the date
+ * @param minute
+ * The value (0-59) to give the minutes field of the date
+ * @param hour
+ * The value (0-23) to give the hours field of the date
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getDateOf(int second, int minute, int hour) {
+ validateSecond(second);
+ validateMinute(minute);
+ validateHour(hour);
+
+ Date date = new Date();
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+ c.setLenient(true);
+
+ c.set(Calendar.HOUR_OF_DAY, hour);
+ c.set(Calendar.MINUTE, minute);
+ c.set(Calendar.SECOND, second);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ *
+ * Get a Date
object that represents the given time, on the
+ * given date.
+ *
+ *
+ * @param second
+ * The value (0-59) to give the seconds field of the date
+ * @param minute
+ * The value (0-59) to give the minutes field of the date
+ * @param hour
+ * The value (0-23) to give the hours field of the date
+ * @param dayOfMonth
+ * The value (1-31) to give the day of month field of the date
+ * @param month
+ * The value (1-12) to give the month field of the date
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getDateOf(int second, int minute, int hour,
+ int dayOfMonth, int month) {
+ validateSecond(second);
+ validateMinute(minute);
+ validateHour(hour);
+ validateDayOfMonth(dayOfMonth);
+ validateMonth(month);
+
+ Date date = new Date();
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ c.set(Calendar.MONTH, month - 1);
+ c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+ c.set(Calendar.HOUR_OF_DAY, hour);
+ c.set(Calendar.MINUTE, minute);
+ c.set(Calendar.SECOND, second);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ *
+ * Get a Date
object that represents the given time, on the
+ * given date.
+ *
+ *
+ * @param second
+ * The value (0-59) to give the seconds field of the date
+ * @param minute
+ * The value (0-59) to give the minutes field of the date
+ * @param hour
+ * The value (0-23) to give the hours field of the date
+ * @param dayOfMonth
+ * The value (1-31) to give the day of month field of the date
+ * @param month
+ * The value (1-12) to give the month field of the date
+ * @param year
+ * The value (1970-2099) to give the year field of the date
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static Date getDateOf(int second, int minute, int hour,
+ int dayOfMonth, int month, int year) {
+ validateSecond(second);
+ validateMinute(minute);
+ validateHour(hour);
+ validateDayOfMonth(dayOfMonth);
+ validateMonth(month);
+ validateYear(year);
+
+ Date date = new Date();
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ c.set(Calendar.YEAR, year);
+ c.set(Calendar.MONTH, month - 1);
+ c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+ c.set(Calendar.HOUR_OF_DAY, hour);
+ c.set(Calendar.MINUTE, minute);
+ c.set(Calendar.SECOND, second);
+ c.set(Calendar.MILLISECOND, 0);
+
+ return c.getTime();
+ }
+
+ /**
+ * Returns a list of Dates that are the next fire times of a Trigger
.
+ * The input trigger will be cloned before any work is done, so you need
+ * not worry about its state being altered by this method.
+ *
+ * @param trigg
+ * The trigger upon which to do the work
+ * @param cal
+ * The calendar to apply to the trigger's schedule
+ * @param numTimes
+ * The number of next fire times to produce
+ * @return List of java.util.Date objects
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ */
+ public static List computeFireTimes(Trigger trigg, com.fr.third.org.quartz.Calendar cal,
+ int numTimes) {
+ LinkedList lst = new LinkedList();
+
+ Trigger t = (Trigger) trigg.clone();
+
+ if (t.getNextFireTime() == null) {
+ t.computeFirstFireTime(cal);
+ }
+
+ for (int i = 0; i < numTimes; i++) {
+ Date d = t.getNextFireTime();
+ if (d != null) {
+ lst.add(d);
+ t.triggered(cal);
+ } else {
+ break;
+ }
+ }
+
+ return java.util.Collections.unmodifiableList(lst);
+ }
+
+ /**
+ * Returns a list of Dates that are the next fire times of a Trigger
+ * that fall within the given date range. The input trigger will be cloned
+ * before any work is done, so you need not worry about its state being
+ * altered by this method.
+ *
+ * @param trigg
+ * The trigger upon which to do the work
+ * @param cal
+ * The calendar to apply to the trigger's schedule
+ * @param from
+ * The starting date at which to find fire times
+ * @param to
+ * The ending date at which to stop finding fire times
+ * @return List of java.util.Date objects
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ *
+ *
+ */
+ public static List computeFireTimesBetween(Trigger trigg,
+ com.fr.third.org.quartz.Calendar cal, Date from, Date to) {
+ LinkedList lst = new LinkedList();
+
+ Trigger t = (Trigger) trigg.clone();
+
+ if (t.getNextFireTime() == null) {
+ t.computeFirstFireTime(cal);
+ }
+
+ // TODO: this method could be more efficient by using logic specific
+ // to the type of trigger ...
+ while (true) {
+ Date d = t.getNextFireTime();
+ if (d != null) {
+ if (d.before(from)) {
+ t.triggered(cal);
+ continue;
+ }
+ if (d.after(to)) {
+ break;
+ }
+ lst.add(d);
+ t.triggered(cal);
+ } else {
+ break;
+ }
+ }
+
+ return java.util.Collections.unmodifiableList(lst);
+ }
+
+ /**
+ * Translate a date & time from a users timezone to the another
+ * (probably server) timezone to assist in creating a simple trigger with
+ * the right date & time.
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ */
+ public static Date translateTime(Date date, TimeZone src, TimeZone dest) {
+
+ Date newDate = new Date();
+
+ int offset = (getOffset(date.getTime(), dest) - getOffset(date.getTime(), src));
+
+ newDate.setTime(date.getTime() - offset);
+
+ return newDate;
+ }
+
+ /**
+ * Gets the offset from UT for the given date in the given timezone,
+ * taking into account daylight savings.
+ *
+ *
+ * Equivalent of TimeZone.getOffset(date) in JDK 1.4, but Quartz is trying
+ * to support JDK 1.3.
+ *
+ *
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ */
+ public static int getOffset(long date, TimeZone tz) {
+
+ if (tz.inDaylightTime(new Date(date))) {
+ return tz.getRawOffset() + getDSTSavings(tz);
+ }
+
+ return tz.getRawOffset();
+ }
+
+ /**
+ *
+ * Equivalent of TimeZone.getDSTSavings() in JDK 1.4, but Quartz is trying
+ * to support JDK 1.3.
+ *
+ *
+ * @deprecated use com.fr.third.org.quartz.TriggerUtils instead!
+ */
+ public static int getDSTSavings(TimeZone tz) {
+
+ if (tz.useDaylightTime()) {
+ return 3600000;
+ }
+ return 0;
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/helpers/VersionPrinter.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/helpers/VersionPrinter.java
new file mode 100644
index 000000000..9b63aeb47
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/helpers/VersionPrinter.java
@@ -0,0 +1,54 @@
+
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.helpers;
+
+import com.fr.third.org.quartz.core.QuartzScheduler;
+
+/**
+ *
+ * Prints the version of Quartz on stdout.
+ *
+ *
+ * @author James House
+ */
+public class VersionPrinter {
+
+ /**
+ * Private constructor because this is a pure utility class.
+ */
+ private VersionPrinter() {
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public static void main(String[] args) {
+ System.out.println("Quartz version: " + QuartzScheduler.getVersionMajor()
+ + "." + QuartzScheduler.getVersionMinor() + "."
+ + QuartzScheduler.getVersionIteration());
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/helpers/package.html b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/helpers/package.html
new file mode 100644
index 000000000..09d4f72d5
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/helpers/package.html
@@ -0,0 +1,15 @@
+
+
+Package com.fr.third.org.quartz.helpers
+
+
+Contains helper classes to make working with Quartz easier.
+
+
+
+
+See the Quartz project
+ at Open Symphony for more information.
+
+
+
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/DirectSchedulerFactory.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/DirectSchedulerFactory.java
new file mode 100644
index 000000000..0bda8dbad
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/DirectSchedulerFactory.java
@@ -0,0 +1,482 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.SchedulerFactory;
+import com.fr.third.org.quartz.core.JobRunShellFactory;
+import com.fr.third.org.quartz.core.QuartzScheduler;
+import com.fr.third.org.quartz.core.QuartzSchedulerResources;
+import com.fr.third.org.quartz.core.SchedulingContext;
+import com.fr.third.org.quartz.simpl.CascadingClassLoadHelper;
+import com.fr.third.org.quartz.simpl.RAMJobStore;
+import com.fr.third.org.quartz.simpl.SimpleThreadPool;
+import com.fr.third.org.quartz.spi.ClassLoadHelper;
+import com.fr.third.org.quartz.spi.JobStore;
+import com.fr.third.org.quartz.spi.SchedulerPlugin;
+import com.fr.third.org.quartz.spi.ThreadPool;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ *
+ * A singleton implementation of {@link com.fr.third.org.quartz.SchedulerFactory}
.
+ *
+ *
+ *
+ * Here are some examples of using this class:
+ *
+ *
+ * To create a scheduler that does not write anything to the database (is not
+ * persistent), you can call createVolatileScheduler
:
+ *
+ *
+ * DirectSchedulerFactory.getInstance().createVolatileScheduler(10); // 10 threads * // don't forget to start the scheduler: DirectSchedulerFactory.getInstance().getScheduler().start();
+ *
+ *
+ *
+ *
+ * Several create methods are provided for convenience. All create methods
+ * eventually end up calling the create method with all the parameters:
+ *
+ *
+ *
+ * public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort)
+ *
+ *
+ *
+ *
+ * Here is an example of using this method:
+ *
+ * *
+ * * // create the thread pool SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads, Thread.NORM_PRIORITY); threadPool.initialize(); * // create the job store JobStore jobStore = new RAMJobStore(); jobStore.initialize();
+ *
+ * DirectSchedulerFactory.getInstance().createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore, "localhost", 1099); * // don't forget to start the scheduler: DirectSchedulerFactory.getInstance().getScheduler("My Quartz Scheduler", "My Instance").start();
+ *
+ *
+ *
+ *
+ * You can also use a JDBCJobStore instead of the RAMJobStore:
+ *
+ *
+ *
+ * DBConnectionManager.getInstance().addConnectionProvider("someDatasource", new JNDIConnectionProvider("someDatasourceJNDIName"));
+ *
+ * JDBCJobStore jdbcJobStore = new JDBCJobStore(); jdbcJobStore.setDataSource("someDatasource"); jdbcJobStore.setPostgresStyleBlobs(true); jdbcJobStore.setTablePrefix("QRTZ_"); jdbcJobStore.setInstanceId("My Instance"); jdbcJobStore.initialize();
+ *
+ *
+ * @author Mohammad Rezaei
+ * @author James House
+ *
+ * @see JobStore
+ * @see ThreadPool
+ */
+public class DirectSchedulerFactory implements SchedulerFactory {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constants.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+ public static final String DEFAULT_INSTANCE_ID = "SIMPLE_NON_CLUSTERED";
+
+ public static final String DEFAULT_SCHEDULER_NAME = "SimpleQuartzScheduler";
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private boolean initialized = false;
+
+ private static DirectSchedulerFactory instance = new DirectSchedulerFactory();
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ protected Log getLog() {
+ return log;
+ }
+
+ /**
+ * Constructor
+ */
+ protected DirectSchedulerFactory() {
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public static DirectSchedulerFactory getInstance() {
+ return instance;
+ }
+
+ /**
+ * Creates an in memory job store ({@link RAMJobStore}
)
+ * The thread priority is set to Thread.NORM_PRIORITY
+ *
+ * @param maxThreads
+ * The number of threads in the thread pool
+ * @throws SchedulerException
+ * if initialization failed.
+ */
+ public void createVolatileScheduler(int maxThreads)
+ throws SchedulerException {
+ SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads,
+ Thread.NORM_PRIORITY);
+ threadPool.initialize();
+ JobStore jobStore = new RAMJobStore();
+ this.createScheduler(threadPool, jobStore);
+
+ }
+
+ /**
+ * @deprecated see correctly spelled method.
+ * @see #createVolatileScheduler(int)
+ */
+ public void createVolatileSchduler(int maxThreads)
+ throws SchedulerException {
+ createVolatileScheduler(maxThreads);
+ }
+
+ /**
+ * Creates a proxy to a remote scheduler. This scheduler can be retrieved
+ * via {@link DirectSchedulerFactory#getScheduler()}
+ *
+ * @param rmiHost
+ * The hostname for remote scheduler
+ * @param rmiPort
+ * Port for the remote scheduler. The default RMI port is 1099.
+ * @throws SchedulerException
+ * if the remote scheduler could not be reached.
+ */
+ public void createRemoteScheduler(String rmiHost, int rmiPort)
+ throws SchedulerException {
+ createRemoteScheduler(DEFAULT_SCHEDULER_NAME, DEFAULT_INSTANCE_ID,
+ rmiHost, rmiPort);
+ initialized = true;
+ }
+
+ /**
+ * Same as
+ * {@link DirectSchedulerFactory#createRemoteScheduler(String rmiHost, int rmiPort)},
+ * with the addition of specifying the scheduler name and instance ID. This
+ * scheduler can only be retrieved via
+ * {@link DirectSchedulerFactory#getScheduler(String)}
+ *
+ * @param schedulerName
+ * The name for the scheduler.
+ * @param schedulerInstanceId
+ * The instance ID for the scheduler.
+ * @param rmiHost
+ * The hostname for remote scheduler
+ * @param rmiPort
+ * Port for the remote scheduler. The default RMI port is 1099.
+ * @throws SchedulerException
+ * if the remote scheduler could not be reached.
+ */
+ public void createRemoteScheduler(String schedulerName,
+ String schedulerInstanceId, String rmiHost, int rmiPort)
+ throws SchedulerException {
+ createRemoteScheduler(schedulerName,
+ schedulerInstanceId, null, rmiHost, rmiPort);
+ }
+
+ /**
+ * Same as
+ * {@link DirectSchedulerFactory#createRemoteScheduler(String rmiHost, int rmiPort)},
+ * with the addition of specifying the scheduler name, instance ID, and rmi
+ * bind name. This scheduler can only be retrieved via
+ * {@link DirectSchedulerFactory#getScheduler(String)}
+ *
+ * @param schedulerName
+ * The name for the scheduler.
+ * @param schedulerInstanceId
+ * The instance ID for the scheduler.
+ * @param rmiBindName
+ * The name of the remote scheduler in the RMI repository. If null
+ * defaults to the generated unique identifier.
+ * @param rmiHost
+ * The hostname for remote scheduler
+ * @param rmiPort
+ * Port for the remote scheduler. The default RMI port is 1099.
+ * @throws SchedulerException
+ * if the remote scheduler could not be reached.
+ */
+ public void createRemoteScheduler(String schedulerName,
+ String schedulerInstanceId, String rmiBindName, String rmiHost, int rmiPort)
+ throws SchedulerException {
+ SchedulingContext schedCtxt = new SchedulingContext();
+ schedCtxt.setInstanceId(schedulerInstanceId);
+
+ String uid = (rmiBindName != null) ? rmiBindName :
+ QuartzSchedulerResources.getUniqueIdentifier(
+ schedulerName, schedulerInstanceId);
+
+ RemoteScheduler remoteScheduler = new RemoteScheduler(schedCtxt, uid,
+ rmiHost, rmiPort);
+
+ SchedulerRepository schedRep = SchedulerRepository.getInstance();
+ schedRep.bind(remoteScheduler);
+ }
+
+ /**
+ * Creates a scheduler using the specified thread pool and job store. This
+ * scheduler can be retrieved via
+ * {@link DirectSchedulerFactory#getScheduler()}
+ *
+ * @param threadPool
+ * The thread pool for executing jobs
+ * @param jobStore
+ * The type of job store
+ * @throws SchedulerException
+ * if initialization failed
+ */
+ public void createScheduler(ThreadPool threadPool, JobStore jobStore)
+ throws SchedulerException {
+ createScheduler(DEFAULT_SCHEDULER_NAME, DEFAULT_INSTANCE_ID,
+ threadPool, jobStore);
+ initialized = true;
+ }
+
+ /**
+ * Same as
+ * {@link DirectSchedulerFactory#createScheduler(ThreadPool threadPool, JobStore jobStore)},
+ * with the addition of specifying the scheduler name and instance ID. This
+ * scheduler can only be retrieved via
+ * {@link DirectSchedulerFactory#getScheduler(String)}
+ *
+ * @param schedulerName
+ * The name for the scheduler.
+ * @param schedulerInstanceId
+ * The instance ID for the scheduler.
+ * @param threadPool
+ * The thread pool for executing jobs
+ * @param jobStore
+ * The type of job store
+ * @throws SchedulerException
+ * if initialization failed
+ */
+ public void createScheduler(String schedulerName,
+ String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore)
+ throws SchedulerException {
+ createScheduler(schedulerName, schedulerInstanceId, threadPool,
+ jobStore, null, 0, -1, -1);
+ }
+
+ /**
+ * Creates a scheduler using the specified thread pool and job store and
+ * binds it to RMI.
+ *
+ * @param schedulerName
+ * The name for the scheduler.
+ * @param schedulerInstanceId
+ * The instance ID for the scheduler.
+ * @param threadPool
+ * The thread pool for executing jobs
+ * @param jobStore
+ * The type of job store
+ * @param rmiRegistryHost
+ * The hostname to register this scheduler with for RMI. Can use
+ * "null" if no RMI is required.
+ * @param rmiRegistryPort
+ * The port for RMI. Typically 1099.
+ * @param idleWaitTime
+ * The idle wait time in milliseconds. You can specify "-1" for
+ * the default value, which is currently 30000 ms.
+ * @throws SchedulerException
+ * if initialization failed
+ */
+ public void createScheduler(String schedulerName,
+ String schedulerInstanceId, ThreadPool threadPool,
+ JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort,
+ long idleWaitTime, long dbFailureRetryInterval)
+ throws SchedulerException {
+ createScheduler(schedulerName,
+ schedulerInstanceId, threadPool,
+ jobStore, null, // plugins
+ rmiRegistryHost, rmiRegistryPort,
+ idleWaitTime, dbFailureRetryInterval);
+ }
+
+ /**
+ * Creates a scheduler using the specified thread pool, job store, and
+ * plugins, and binds it to RMI.
+ *
+ * @param schedulerName
+ * The name for the scheduler.
+ * @param schedulerInstanceId
+ * The instance ID for the scheduler.
+ * @param threadPool
+ * The thread pool for executing jobs
+ * @param jobStore
+ * The type of job store
+ * @param schedulerPluginMap
+ * Map from a String
plugin names to
+ * {@link com.fr.third.org.quartz.spi.SchedulerPlugin}
s. Can use
+ * "null" if no plugins are required.
+ * @param rmiRegistryHost
+ * The hostname to register this scheduler with for RMI. Can use
+ * "null" if no RMI is required.
+ * @param rmiRegistryPort
+ * The port for RMI. Typically 1099.
+ * @param idleWaitTime
+ * The idle wait time in milliseconds. You can specify "-1" for
+ * the default value, which is currently 30000 ms.
+ * @throws SchedulerException
+ * if initialization failed
+ */
+ public void createScheduler(String schedulerName,
+ String schedulerInstanceId, ThreadPool threadPool,
+ JobStore jobStore, Map schedulerPluginMap,
+ String rmiRegistryHost, int rmiRegistryPort,
+ long idleWaitTime, long dbFailureRetryInterval)
+ throws SchedulerException {
+ // Currently only one run-shell factory is available...
+ JobRunShellFactory jrsf = new StdJobRunShellFactory();
+
+ // Fire everything up
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ SchedulingContext schedCtxt = new SchedulingContext();
+ schedCtxt.setInstanceId(schedulerInstanceId);
+
+ QuartzSchedulerResources qrs = new QuartzSchedulerResources();
+
+ qrs.setName(schedulerName);
+ qrs.setInstanceId(schedulerInstanceId);
+ qrs.setJobRunShellFactory(jrsf);
+ qrs.setThreadPool(threadPool);
+ qrs.setJobStore(jobStore);
+ qrs.setRMIRegistryHost(rmiRegistryHost);
+ qrs.setRMIRegistryPort(rmiRegistryPort);
+
+ // add plugins
+ if (schedulerPluginMap != null) {
+ for (Iterator pluginIter = schedulerPluginMap.values().iterator(); pluginIter.hasNext();) {
+ qrs.addSchedulerPlugin((SchedulerPlugin)pluginIter.next());
+ }
+ }
+
+ QuartzScheduler qs = new QuartzScheduler(qrs, schedCtxt, idleWaitTime,
+ dbFailureRetryInterval);
+
+ ClassLoadHelper cch = new CascadingClassLoadHelper();
+ cch.initialize();
+
+ jobStore.initialize(cch, qs.getSchedulerSignaler());
+
+ Scheduler scheduler = new StdScheduler(qs, schedCtxt);
+
+ // Initialize plugins now that we have a Scheduler instance.
+ if (schedulerPluginMap != null) {
+ for (Iterator pluginEntryIter = schedulerPluginMap.entrySet().iterator(); pluginEntryIter.hasNext();) {
+ Map.Entry pluginEntry = (Map.Entry)pluginEntryIter.next();
+
+ ((SchedulerPlugin)pluginEntry.getValue()).initialize(
+ (String)pluginEntry.getKey(), scheduler);
+ }
+ }
+
+ jrsf.initialize(scheduler, schedCtxt);
+
+ getLog().info("Quartz scheduler '" + scheduler.getSchedulerName());
+
+ getLog().info("Quartz scheduler version: " + qs.getVersion());
+
+ SchedulerRepository schedRep = SchedulerRepository.getInstance();
+
+ qs.addNoGCObject(schedRep); // prevents the repository from being
+ // garbage collected
+
+ schedRep.bind(scheduler);
+ }
+
+ /*
+ * public void registerSchedulerForRmi(String schedulerName, String
+ * schedulerId, String registryHost, int registryPort) throws
+ * SchedulerException, RemoteException { QuartzScheduler scheduler =
+ * (QuartzScheduler) this.getScheduler(); scheduler.bind(registryHost,
+ * registryPort); }
+ */
+
+ /**
+ *
+ * Returns a handle to the Scheduler produced by this factory.
+ *
+ *
+ *
+ * you must call createRemoteScheduler or createScheduler methods before
+ * calling getScheduler()
+ *
+ */
+ public Scheduler getScheduler() throws SchedulerException {
+ if (!initialized) {
+ throw new SchedulerException(
+ "you must call createRemoteScheduler or createScheduler methods before calling getScheduler()");
+ }
+
+ return getScheduler(DEFAULT_SCHEDULER_NAME);
+ }
+
+ /**
+ *
+ * Returns a handle to the Scheduler with the given name, if it exists.
+ *
+ */
+ public Scheduler getScheduler(String schedName) throws SchedulerException {
+ SchedulerRepository schedRep = SchedulerRepository.getInstance();
+
+ return schedRep.lookup(schedName);
+ }
+
+ /**
+ *
+ * Returns a handle to all known Schedulers (made by any
+ * StdSchedulerFactory instance.).
+ *
+ */
+ public Collection getAllSchedulers() throws SchedulerException {
+ return SchedulerRepository.getInstance().lookupAll();
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/QuartzServer.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/QuartzServer.java
new file mode 100644
index 000000000..6879b4e90
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/QuartzServer.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.SchedulerFactory;
+import com.fr.third.org.quartz.listeners.SchedulerListenerSupport;
+
+/**
+ *
+ * Instantiates an instance of Quartz Scheduler as a stand-alone program, if
+ * the scheduler is configured for RMI it will be made available.
+ *
+ *
+ *
+ * The main() method of this class currently accepts 0 or 1 arguemtns, if there
+ * is an argument, and its value is "console"
, then the program
+ * will print a short message on the console (std-out) and wait for the user to
+ * type "exit" - at which time the scheduler will be shutdown.
+ *
+ *
+ *
+ * Future versions of this server should allow additional configuration for
+ * responding to scheduler events by allowing the user to specify {@link com.fr.third.org.quartz.JobListener}
,
+ * {@link com.fr.third.org.quartz.TriggerListener}
and {@link com.fr.third.org.quartz.SchedulerListener}
+ * classes.
+ *
+ *
+ *
+ * Please read the Quartz FAQ entries about RMI before asking questions in the
+ * forums or mail-lists.
+ *
+ *
+ * @author James House
+ */
+public class QuartzServer extends SchedulerListenerSupport {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private Scheduler sched = null;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ QuartzServer() {
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public void serve(SchedulerFactory schedFact, boolean console)
+ throws Exception {
+ sched = schedFact.getScheduler();
+
+ sched.start();
+
+ try {
+ Thread.sleep(3000l);
+ } catch (Exception ignore) {
+ }
+
+ System.out.println("\n*** The scheduler successfully started.");
+
+ if (console) {
+ System.out.println("\n");
+ System.out
+ .println("The scheduler will now run until you type \"exit\"");
+ System.out
+ .println(" If it was configured to export itself via RMI,");
+ System.out.println(" then other process may now use it.");
+
+ BufferedReader rdr = new BufferedReader(new InputStreamReader(
+ System.in));
+
+ while (true) {
+ System.out.print("Type 'exit' to shutdown the server: ");
+ if ("exit".equals(rdr.readLine())) {
+ break;
+ }
+ }
+
+ System.out.println("\n...Shutting down server...");
+
+ sched.shutdown(true);
+ }
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * SchedulerListener Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Called by the {@link Scheduler}
when a serious error has
+ * occured within the scheduler - such as repeated failures in the JobStore
,
+ * or the inability to instantiate a Job
instance when its
+ * Trigger
has fired.
+ *
+ *
+ *
+ * The getErrorCode()
method of the given SchedulerException
+ * can be used to determine more specific information about the type of
+ * error that was encountered.
+ *
+ */
+ public void schedulerError(String msg, SchedulerException cause) {
+ System.err.println("*** " + msg);
+ cause.printStackTrace();
+ }
+
+ /**
+ *
+ * Called by the {@link Scheduler}
to inform the listener
+ * that it has shutdown.
+ *
+ */
+ public void schedulerShutdown() {
+ System.out.println("\n*** The scheduler is now shutdown.");
+ sched = null;
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Main Method.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public static void main(String[] args) throws Exception {
+
+ // //Configure Log4J
+ // org.apache.log4j.PropertyConfigurator.configure(
+ // System.getProperty("log4jConfigFile", "log4j.properties"));
+
+ if (System.getSecurityManager() == null) {
+ System.setSecurityManager(new java.rmi.RMISecurityManager());
+ }
+
+ try {
+ QuartzServer server = new QuartzServer();
+ if (args.length == 0) {
+ server.serve(
+ new com.fr.third.org.quartz.impl.StdSchedulerFactory(), false);
+ } else if (args.length == 1 && args[0].equalsIgnoreCase("console")) {
+ server.serve(new com.fr.third.org.quartz.impl.StdSchedulerFactory(), true);
+ } else {
+ System.err.println("\nUsage: QuartzServer [console]");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/RemoteMBeanScheduler.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/RemoteMBeanScheduler.java
new file mode 100644
index 000000000..ef7583170
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/RemoteMBeanScheduler.java
@@ -0,0 +1,1108 @@
+/*
+ * Copyright 2004-2006 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package com.fr.third.org.quartz.impl;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import javax.management.AttributeList;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import com.fr.third.org.quartz.Calendar;
+import com.fr.third.org.quartz.JobDataMap;
+import com.fr.third.org.quartz.JobDetail;
+import com.fr.third.org.quartz.JobListener;
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerContext;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.SchedulerListener;
+import com.fr.third.org.quartz.SchedulerMetaData;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.TriggerListener;
+import com.fr.third.org.quartz.UnableToInterruptJobException;
+import com.fr.third.org.quartz.core.SchedulingContext;
+import com.fr.third.org.quartz.spi.JobFactory;
+
+/**
+ *
+ * An implementation of the Scheduler
interface that remotely
+ * proxies all method calls to the equivalent call on a given QuartzScheduler
+ * instance, via JMX.
+ *
+ *
+ *
+ * A user must create a subclass to implement the actual connection to the remote
+ * MBeanServer using their application specific connector.
+ * For example {@link com.fr.third.org.quartz.ee.jmx.jboss.JBoss4RMIRemoteMBeanScheduler}
.
+ *
+ * @see com.fr.third.org.quartz.Scheduler
+ * @see com.fr.third.org.quartz.core.QuartzScheduler
+ * @see com.fr.third.org.quartz.core.SchedulingContext
+ */
+public abstract class RemoteMBeanScheduler implements Scheduler {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private SchedulingContext schedulingContext;
+
+ private ObjectName schedulerObjectName;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public RemoteMBeanScheduler() {
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Properties.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Get the name under which the Scheduler MBean is registered on the
+ * remote MBean server.
+ */
+ protected ObjectName getSchedulerObjectName() {
+ return schedulerObjectName;
+ }
+
+ /**
+ * Set the name under which the Scheduler MBean is registered on the
+ * remote MBean server.
+ */
+ public void setSchedulerObjectName(String schedulerObjectName) throws SchedulerException {
+ try {
+ this.schedulerObjectName = new ObjectName(schedulerObjectName);
+ } catch (MalformedObjectNameException e) {
+ throw new SchedulerException("Failed to parse Scheduler MBean name: " + schedulerObjectName, e);
+ }
+ }
+
+ /**
+ * Set the name under which the Scheduler MBean is registered on the
+ * remote MBean server.
+ */
+ public void setSchedulerObjectName(ObjectName schedulerObjectName) throws SchedulerException {
+ this.schedulerObjectName = schedulerObjectName;
+ }
+
+ /**
+ * Set the scheduling context of this proxy.
+ */
+ public void setSchedulingContext(SchedulingContext schedulingContext) {
+ this.schedulingContext = schedulingContext;
+ }
+
+
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Abstract methods.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Initialize this RemoteMBeanScheduler instance, connecting to the
+ * remote MBean server.
+ */
+ public abstract void initialize() throws SchedulerException;
+
+ /**
+ * Get the given attribute of the remote Scheduler MBean.
+ */
+ protected abstract Object getAttribute(
+ String attribute) throws SchedulerException;
+
+ /**
+ * Get the given attributes of the remote Scheduler MBean.
+ */
+ protected abstract AttributeList getAttributes(String[] attributes)
+ throws SchedulerException;
+
+ /**
+ * Invoke the given operation on the remote Scheduler MBean.
+ */
+ protected abstract Object invoke(
+ String operationName,
+ Object[] params,
+ String[] signature) throws SchedulerException;
+
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Returns the name of the Scheduler
.
+ *
+ */
+ public String getSchedulerName() throws SchedulerException {
+ return (String)getAttribute("schedulerName");
+ }
+
+ /**
+ *
+ * Returns the instance Id of the Scheduler
.
+ *
+ */
+ public String getSchedulerInstanceId() throws SchedulerException {
+ return (String)getAttribute("schedulerInstanceId");
+ }
+
+ public SchedulerMetaData getMetaData() throws SchedulerException {
+ AttributeList attributeList =
+ getAttributes(
+ new String[] {
+ "schedulerName",
+ "schedulerInstanceId",
+ "inStandbyMode",
+ "shutdown",
+ "jobStoreClass",
+ "threadPoolClass",
+ "threadPoolSize",
+ "version"
+ });
+
+ return new SchedulerMetaData(
+ (String)attributeList.get(0),
+ (String)attributeList.get(1),
+ getClass(), true, isStarted(),
+ ((Boolean)attributeList.get(2)).booleanValue(),
+ ((Boolean)attributeList.get(3)).booleanValue(),
+ (Date)invoke("runningSince", new Object[] {}, new String[] {}),
+ ((Integer)invoke("numJobsExecuted", new Object[] {}, new String[] {})).intValue(),
+ (Class)attributeList.get(4),
+ ((Boolean)invoke("supportsPersistence", new Object[] {}, new String[] {})).booleanValue(),
+ (Class)attributeList.get(5),
+ ((Integer)attributeList.get(6)).intValue(),
+ (String)attributeList.get(7));
+ }
+
+ /**
+ *
+ * Returns the SchedulerContext
of the Scheduler
.
+ *
+ */
+ public SchedulerContext getContext() throws SchedulerException {
+ return (SchedulerContext)getAttribute("schedulerContext");
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///
+ /// Schedululer State Management Methods
+ ///
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void start() throws SchedulerException {
+ invoke("start", new Object[] {}, new String[] {});
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void startDelayed(int seconds) throws SchedulerException {
+ invoke("startDelayed", new Object[] {new Integer(seconds)}, new String[] {int.class.getName()});
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void standby() throws SchedulerException {
+ invoke("standby", new Object[] {}, new String[] {});
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Scheduler#pause()
+ * @deprecated
+ */
+ public void pause() throws SchedulerException {
+ standby();
+ }
+
+
+ /**
+ * Whether the scheduler has been started.
+ *
+ *
+ * Note: This only reflects whether {@link #start()}
has ever
+ * been called on this Scheduler, so it will return true
even
+ * if the Scheduler
is currently in standby mode or has been
+ * since shutdown.
+ *
+ *
+ * @see #start()
+ * @see #isShutdown()
+ * @see #isInStandbyMode()
+ */
+ public boolean isStarted() throws SchedulerException {
+ return (invoke("runningSince", new Object[] {}, new String[] {}) != null);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean isInStandbyMode() throws SchedulerException {
+ return ((Boolean)getAttribute("inStandbyMode")).booleanValue();
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Scheduler#isInStandbyMode()
+ * @deprecated
+ */
+ public boolean isPaused() throws SchedulerException {
+ return isInStandbyMode();
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void shutdown() throws SchedulerException {
+ // Have to get the scheduler name before we actually call shutdown.
+ String schedulerName = getSchedulerName();
+
+ invoke("shutdown", new Object[] {}, new String[] {});
+ SchedulerRepository.getInstance().remove(schedulerName);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void shutdown(boolean waitForJobsToComplete)
+ throws SchedulerException {
+ // Have to get the scheduler name before we actually call shutdown.
+ String schedulerName = getSchedulerName();
+
+ invoke(
+ "shutdown",
+ new Object[] { toBoolean(waitForJobsToComplete) },
+ new String[] { boolean.class.getName() });
+
+ SchedulerRepository.getInstance().remove(schedulerName);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean isShutdown() throws SchedulerException {
+ return ((Boolean)getAttribute("shutdown")).booleanValue();
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public List getCurrentlyExecutingJobs() throws SchedulerException {
+ return (List)invoke("getCurrentlyExecutingJobs", new Object[] {}, new String[] {});
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///
+ /// Scheduling-related Methods
+ ///
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Date scheduleJob(JobDetail jobDetail, Trigger trigger)
+ throws SchedulerException {
+ return (Date)invoke(
+ "scheduleJob",
+ new Object[] { schedulingContext, jobDetail, trigger },
+ new String[] { SchedulingContext.class.getName(), JobDetail.class.getName(), Trigger.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Date scheduleJob(Trigger trigger) throws SchedulerException {
+ return (Date)invoke(
+ "scheduleJob",
+ new Object[] { schedulingContext, trigger },
+ new String[] { SchedulingContext.class.getName(), Trigger.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void addJob(JobDetail jobDetail, boolean replace)
+ throws SchedulerException {
+ invoke(
+ "addJob",
+ new Object[] { schedulingContext, jobDetail, toBoolean(replace) },
+ new String[] { SchedulingContext.class.getName(), JobDetail.class.getName(), boolean.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public boolean deleteJob(String jobName, String groupName)
+ throws SchedulerException {
+ return ((Boolean)invoke(
+ "deleteJob",
+ new Object[] { schedulingContext, jobName, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() })).booleanValue();
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public boolean unscheduleJob(String triggerName, String groupName)
+ throws SchedulerException {
+ return ((Boolean)invoke(
+ "unscheduleJob",
+ new Object[] { schedulingContext, triggerName, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() })).booleanValue();
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Date rescheduleJob(String triggerName,
+ String groupName, Trigger newTrigger) throws SchedulerException {
+ return (Date)invoke(
+ "unscheduleJob",
+ new Object[] { schedulingContext, triggerName, groupName, newTrigger},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName(), Trigger.class.getName() });
+ }
+
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void triggerJob(String jobName, String groupName)
+ throws SchedulerException {
+ triggerJob(jobName, groupName, null);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void triggerJob(String jobName, String groupName, JobDataMap data)
+ throws SchedulerException {
+ invoke(
+ "triggerJob",
+ new Object[] { schedulingContext, jobName, groupName, data},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName(), JobDataMap.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void triggerJobWithVolatileTrigger(String jobName, String groupName)
+ throws SchedulerException {
+ triggerJobWithVolatileTrigger(jobName, groupName, null);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void triggerJobWithVolatileTrigger(String jobName, String groupName, JobDataMap data)
+ throws SchedulerException {
+ invoke(
+ "triggerJobWithVolatileTrigger",
+ new Object[] { schedulingContext, jobName, groupName, data},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName(), JobDataMap.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseTrigger(String triggerName, String groupName)
+ throws SchedulerException {
+ invoke(
+ "pauseTrigger",
+ new Object[] { schedulingContext, triggerName, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseTriggerGroup(String groupName) throws SchedulerException {
+ invoke(
+ "pauseTriggerGroup",
+ new Object[] { schedulingContext, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseJob(String jobName, String groupName)
+ throws SchedulerException {
+ invoke(
+ "pauseJob",
+ new Object[] { schedulingContext, jobName, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseJobGroup(String groupName) throws SchedulerException {
+ invoke(
+ "pauseJobGroup",
+ new Object[] { schedulingContext, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeTrigger(String triggerName, String groupName)
+ throws SchedulerException {
+ invoke(
+ "resumeTrigger",
+ new Object[] { schedulingContext, triggerName, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeTriggerGroup(String groupName) throws SchedulerException {
+ invoke(
+ "resumeTriggerGroup",
+ new Object[] { schedulingContext, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeJob(String jobName, String groupName)
+ throws SchedulerException {
+ invoke(
+ "resumeJob",
+ new Object[] { schedulingContext, jobName, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeJobGroup(String groupName) throws SchedulerException {
+ invoke(
+ "resumeJobGroup",
+ new Object[] { schedulingContext, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseAll() throws SchedulerException {
+ invoke(
+ "pauseAll",
+ new Object[] { schedulingContext},
+ new String[] { SchedulingContext.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeAll() throws SchedulerException {
+ invoke(
+ "resumeAll",
+ new Object[] { schedulingContext},
+ new String[] { SchedulingContext.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getJobGroupNames() throws SchedulerException {
+ return (String[])invoke(
+ "getJobGroupNames",
+ new Object[] { schedulingContext},
+ new String[] { SchedulingContext.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getJobNames(String groupName) throws SchedulerException {
+ return (String[])invoke(
+ "getJobNames",
+ new Object[] { schedulingContext, groupName },
+ new String[] { SchedulingContext.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Trigger[] getTriggersOfJob(String jobName, String groupName)
+ throws SchedulerException {
+ return (Trigger[])invoke(
+ "getTriggersOfJob",
+ new Object[] { schedulingContext, jobName, groupName },
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getTriggerGroupNames() throws SchedulerException {
+ return (String[])invoke(
+ "getTriggerGroupNames",
+ new Object[] { schedulingContext},
+ new String[] { SchedulingContext.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getTriggerNames(String groupName) throws SchedulerException {
+ return (String[])invoke(
+ "getTriggerNames",
+ new Object[] { schedulingContext, groupName },
+ new String[] { SchedulingContext.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public JobDetail getJobDetail(String jobName, String jobGroup)
+ throws SchedulerException {
+ return (JobDetail)invoke(
+ "getJobDetail",
+ new Object[] { schedulingContext, jobName, jobGroup },
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Trigger getTrigger(String triggerName, String triggerGroup)
+ throws SchedulerException {
+ return (Trigger)invoke(
+ "getTrigger",
+ new Object[] { schedulingContext, triggerName, triggerGroup },
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public int getTriggerState(String triggerName, String triggerGroup)
+ throws SchedulerException {
+ return ((Integer)invoke(
+ "getTriggerState",
+ new Object[] { schedulingContext, triggerName, triggerGroup },
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() })).intValue();
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers)
+ throws SchedulerException {
+ invoke(
+ "addCalendar",
+ new Object[] { schedulingContext, calName, calendar, toBoolean(replace), toBoolean(updateTriggers) },
+ new String[] { SchedulingContext.class.getName(), String.class.getName(),
+ Calendar.class.getName(), boolean.class.getName(), boolean.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public boolean deleteCalendar(String calName) throws SchedulerException {
+ return ((Boolean)invoke(
+ "getTriggerState",
+ new Object[] { schedulingContext, calName },
+ new String[] { SchedulingContext.class.getName(), String.class.getName() })).booleanValue();
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Calendar getCalendar(String calName) throws SchedulerException {
+ return (Calendar)invoke(
+ "getCalendar",
+ new Object[] { schedulingContext, calName },
+ new String[] { SchedulingContext.class.getName(), String.class.getName() });
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getCalendarNames() throws SchedulerException {
+ return (String[])invoke(
+ "getCalendarNames",
+ new Object[] { schedulingContext },
+ new String[] { SchedulingContext.class.getName() });
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///
+ /// Listener-related Methods
+ ///
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addGlobalJobListener(JobListener jobListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addJobListener(JobListener jobListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * @deprecated Use {@link #removeGlobalJobListener(String)}
+ */
+ public boolean removeGlobalJobListener(JobListener jobListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeGlobalJobListener(String name)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeJobListener(String name) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public List getGlobalJobListeners() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public Set getJobListenerNames() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public JobListener getGlobalJobListener(String name) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public JobListener getJobListener(String name) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addGlobalTriggerListener(TriggerListener triggerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addTriggerListener(TriggerListener triggerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * @deprecated Use {@link #removeGlobalTriggerListener(String)}
+ */
+ public boolean removeGlobalTriggerListener(TriggerListener triggerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeGlobalTriggerListener(String name)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeTriggerListener(String name) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public List getGlobalTriggerListeners() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public Set getTriggerListenerNames() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public TriggerListener getGlobalTriggerListener(String name)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public TriggerListener getTriggerListener(String name)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addSchedulerListener(SchedulerListener schedulerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeSchedulerListener(SchedulerListener schedulerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public List getSchedulerListeners() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Scheduler#getPausedTriggerGroups()
+ */
+ public Set getPausedTriggerGroups() throws SchedulerException {
+ return (Set)invoke(
+ "getPausedTriggerGroups",
+ new Object[] { schedulingContext },
+ new String[] { SchedulingContext.class.getName() });
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Scheduler#interrupt(java.lang.String, java.lang.String)
+ */
+ public boolean interrupt(String jobName, String groupName) throws UnableToInterruptJobException {
+ try {
+ return ((Boolean)invoke(
+ "interrupt",
+ new Object[] { schedulingContext, jobName, groupName},
+ new String[] { SchedulingContext.class.getName(), String.class.getName(), String.class.getName() })).booleanValue();
+ } catch (SchedulerException se) {
+ throw new UnableToInterruptJobException(se);
+ }
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Scheduler#setJobFactory(com.fr.third.org.quartz.spi.JobFactory)
+ */
+ public void setJobFactory(JobFactory factory) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ protected Boolean toBoolean(boolean bool) {
+ return (bool) ? Boolean.TRUE : Boolean.FALSE;
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/RemoteScheduler.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/RemoteScheduler.java
new file mode 100644
index 000000000..2c816237d
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/RemoteScheduler.java
@@ -0,0 +1,1188 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl;
+
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import com.fr.third.org.quartz.Calendar;
+import com.fr.third.org.quartz.JobDataMap;
+import com.fr.third.org.quartz.JobDetail;
+import com.fr.third.org.quartz.JobListener;
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerContext;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.SchedulerListener;
+import com.fr.third.org.quartz.SchedulerMetaData;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.TriggerListener;
+import com.fr.third.org.quartz.UnableToInterruptJobException;
+import com.fr.third.org.quartz.core.RemotableQuartzScheduler;
+import com.fr.third.org.quartz.core.SchedulingContext;
+import com.fr.third.org.quartz.spi.JobFactory;
+
+/**
+ *
+ * An implementation of the Scheduler
interface that remotely
+ * proxies all method calls to the equivalent call on a given QuartzScheduler
+ * instance, via RMI.
+ *
+ *
+ * @see com.fr.third.org.quartz.Scheduler
+ * @see com.fr.third.org.quartz.core.QuartzScheduler
+ * @see com.fr.third.org.quartz.core.SchedulingContext
+ *
+ * @author James House
+ */
+public class RemoteScheduler implements Scheduler {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private RemotableQuartzScheduler rsched;
+
+ private SchedulingContext schedCtxt;
+
+ private String schedId;
+
+ private String rmiHost;
+
+ private int rmiPort;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Construct a RemoteScheduler
instance to proxy the given
+ * RemoteableQuartzScheduler
instance, and with the given
+ * SchedulingContext
.
+ *
+ */
+ public RemoteScheduler(SchedulingContext schedCtxt, String schedId,
+ String host, int port) {
+
+ this.schedCtxt = schedCtxt;
+ this.schedId = schedId;
+ this.rmiHost = host;
+ this.rmiPort = port;
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ protected RemotableQuartzScheduler getRemoteScheduler()
+ throws SchedulerException {
+ if (rsched != null) {
+ return rsched;
+ }
+
+ try {
+ Registry registry = LocateRegistry.getRegistry(rmiHost, rmiPort);
+
+ rsched = (RemotableQuartzScheduler) registry.lookup(schedId);
+
+ } catch (Exception e) {
+ SchedulerException initException = new SchedulerException(
+ "Could not get handle to remote scheduler: "
+ + e.getMessage(), e);
+ initException
+ .setErrorCode(SchedulerException.ERR_COMMUNICATION_FAILURE);
+ throw initException;
+ }
+
+ return rsched;
+ }
+
+ protected SchedulerException invalidateHandleCreateException(String msg,
+ Exception cause) {
+ rsched = null;
+ SchedulerException ex = new SchedulerException(msg, cause);
+ ex.setErrorCode(SchedulerException.ERR_COMMUNICATION_FAILURE);
+ return ex;
+ }
+
+ /**
+ *
+ * Returns the name of the Scheduler
.
+ *
+ */
+ public String getSchedulerName() throws SchedulerException {
+ try {
+ return getRemoteScheduler().getSchedulerName();
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Returns the instance Id of the Scheduler
.
+ *
+ */
+ public String getSchedulerInstanceId() throws SchedulerException {
+ try {
+ return getRemoteScheduler().getSchedulerInstanceId();
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ public SchedulerMetaData getMetaData() throws SchedulerException {
+ try {
+ RemotableQuartzScheduler sched = getRemoteScheduler();
+ return new SchedulerMetaData(getSchedulerName(),
+ getSchedulerInstanceId(), getClass(), true, isStarted(),
+ isInStandbyMode(), isShutdown(), sched.runningSince(),
+ sched.numJobsExecuted(), sched.getJobStoreClass(),
+ sched.supportsPersistence(), sched.getThreadPoolClass(),
+ sched.getThreadPoolSize(), sched.getVersion());
+
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+
+ }
+
+ /**
+ *
+ * Returns the SchedulerContext
of the Scheduler
.
+ *
+ */
+ public SchedulerContext getContext() throws SchedulerException {
+ try {
+ return getRemoteScheduler().getSchedulerContext();
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///
+ /// Schedululer State Management Methods
+ ///
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void start() throws SchedulerException {
+ try {
+ getRemoteScheduler().start();
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void startDelayed(int seconds) throws SchedulerException {
+ try {
+ getRemoteScheduler().startDelayed(seconds);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void standby() throws SchedulerException {
+ try {
+ getRemoteScheduler().standby();
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Scheduler#pause()
+ * @deprecated
+ */
+ public void pause() throws SchedulerException {
+ this.standby();
+ }
+
+
+
+ /**
+ * Whether the scheduler has been started.
+ *
+ *
+ * Note: This only reflects whether {@link #start()}
has ever
+ * been called on this Scheduler, so it will return true
even
+ * if the Scheduler
is currently in standby mode or has been
+ * since shutdown.
+ *
+ *
+ * @see #start()
+ * @see #isShutdown()
+ * @see #isInStandbyMode()
+ */
+ public boolean isStarted() throws SchedulerException {
+ try {
+ return (getRemoteScheduler().runningSince() != null);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean isInStandbyMode() throws SchedulerException {
+ try {
+ return getRemoteScheduler().isInStandbyMode();
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ public boolean isPaused() throws SchedulerException {
+ return this.isInStandbyMode();
+ }
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void shutdown() throws SchedulerException {
+ try {
+ String schedulerName = getSchedulerName();
+
+ getRemoteScheduler().shutdown();
+
+ SchedulerRepository.getInstance().remove(schedulerName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void shutdown(boolean waitForJobsToComplete)
+ throws SchedulerException {
+ try {
+ String schedulerName = getSchedulerName();
+
+ getRemoteScheduler().shutdown(waitForJobsToComplete);
+
+ SchedulerRepository.getInstance().remove(schedulerName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean isShutdown() throws SchedulerException {
+ try {
+ return getRemoteScheduler().isShutdown();
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public List getCurrentlyExecutingJobs() throws SchedulerException {
+ try {
+ return getRemoteScheduler().getCurrentlyExecutingJobs();
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///
+ /// Scheduling-related Methods
+ ///
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Date scheduleJob(JobDetail jobDetail, Trigger trigger)
+ throws SchedulerException {
+ try {
+ return getRemoteScheduler().scheduleJob(schedCtxt, jobDetail,
+ trigger);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Date scheduleJob(Trigger trigger) throws SchedulerException {
+ try {
+ return getRemoteScheduler().scheduleJob(schedCtxt, trigger);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void addJob(JobDetail jobDetail, boolean replace)
+ throws SchedulerException {
+ try {
+ getRemoteScheduler().addJob(schedCtxt, jobDetail, replace);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public boolean deleteJob(String jobName, String groupName)
+ throws SchedulerException {
+ try {
+ return getRemoteScheduler()
+ .deleteJob(schedCtxt, jobName, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public boolean unscheduleJob(String triggerName, String groupName)
+ throws SchedulerException {
+ try {
+ return getRemoteScheduler().unscheduleJob(schedCtxt, triggerName,
+ groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Date rescheduleJob(String triggerName,
+ String groupName, Trigger newTrigger) throws SchedulerException {
+ try {
+ return getRemoteScheduler().rescheduleJob(schedCtxt, triggerName,
+ groupName, newTrigger);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void triggerJob(String jobName, String groupName)
+ throws SchedulerException {
+ triggerJob(jobName, groupName, null);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void triggerJob(String jobName, String groupName, JobDataMap data)
+ throws SchedulerException {
+ try {
+ getRemoteScheduler().triggerJob(schedCtxt, jobName, groupName, data);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void triggerJobWithVolatileTrigger(String jobName, String groupName)
+ throws SchedulerException {
+ triggerJobWithVolatileTrigger(jobName, groupName, null);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void triggerJobWithVolatileTrigger(String jobName, String groupName, JobDataMap data)
+ throws SchedulerException {
+ try {
+ getRemoteScheduler().triggerJobWithVolatileTrigger(schedCtxt,
+ jobName, groupName, data);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseTrigger(String triggerName, String groupName)
+ throws SchedulerException {
+ try {
+ getRemoteScheduler()
+ .pauseTrigger(schedCtxt, triggerName, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseTriggerGroup(String groupName) throws SchedulerException {
+ try {
+ getRemoteScheduler().pauseTriggerGroup(schedCtxt, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseJob(String jobName, String groupName)
+ throws SchedulerException {
+ try {
+ getRemoteScheduler().pauseJob(schedCtxt, jobName, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseJobGroup(String groupName) throws SchedulerException {
+ try {
+ getRemoteScheduler().pauseJobGroup(schedCtxt, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeTrigger(String triggerName, String groupName)
+ throws SchedulerException {
+ try {
+ getRemoteScheduler().resumeTrigger(schedCtxt, triggerName,
+ groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeTriggerGroup(String groupName) throws SchedulerException {
+ try {
+ getRemoteScheduler().resumeTriggerGroup(schedCtxt, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeJob(String jobName, String groupName)
+ throws SchedulerException {
+ try {
+ getRemoteScheduler().resumeJob(schedCtxt, jobName, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeJobGroup(String groupName) throws SchedulerException {
+ try {
+ getRemoteScheduler().resumeJobGroup(schedCtxt, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void pauseAll() throws SchedulerException {
+ try {
+ getRemoteScheduler().pauseAll(schedCtxt);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void resumeAll() throws SchedulerException {
+ try {
+ getRemoteScheduler().resumeAll(schedCtxt);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getJobGroupNames() throws SchedulerException {
+ try {
+ return getRemoteScheduler().getJobGroupNames(schedCtxt);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getJobNames(String groupName) throws SchedulerException {
+ try {
+ return getRemoteScheduler().getJobNames(schedCtxt, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Trigger[] getTriggersOfJob(String jobName, String groupName)
+ throws SchedulerException {
+ try {
+ return getRemoteScheduler().getTriggersOfJob(schedCtxt, jobName,
+ groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getTriggerGroupNames() throws SchedulerException {
+ try {
+ return getRemoteScheduler().getTriggerGroupNames(schedCtxt);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getTriggerNames(String groupName) throws SchedulerException {
+ try {
+ return getRemoteScheduler().getTriggerNames(schedCtxt, groupName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public JobDetail getJobDetail(String jobName, String jobGroup)
+ throws SchedulerException {
+ try {
+ return getRemoteScheduler().getJobDetail(schedCtxt, jobName,
+ jobGroup);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Trigger getTrigger(String triggerName, String triggerGroup)
+ throws SchedulerException {
+ try {
+ return getRemoteScheduler().getTrigger(schedCtxt, triggerName,
+ triggerGroup);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public int getTriggerState(String triggerName, String triggerGroup)
+ throws SchedulerException {
+ try {
+ return getRemoteScheduler().getTriggerState(schedCtxt, triggerName,
+ triggerGroup);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers)
+ throws SchedulerException {
+ try {
+ getRemoteScheduler().addCalendar(schedCtxt, calName, calendar,
+ replace, updateTriggers);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public boolean deleteCalendar(String calName) throws SchedulerException {
+ try {
+ return getRemoteScheduler().deleteCalendar(schedCtxt, calName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public Calendar getCalendar(String calName) throws SchedulerException {
+ try {
+ return getRemoteScheduler().getCalendar(schedCtxt, calName);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ */
+ public String[] getCalendarNames() throws SchedulerException {
+ try {
+ return getRemoteScheduler().getCalendarNames(schedCtxt);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///
+ /// Listener-related Methods
+ ///
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addGlobalJobListener(JobListener jobListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addJobListener(JobListener jobListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * @deprecated Use {@link #removeGlobalJobListener(String)}
+ */
+ public boolean removeGlobalJobListener(JobListener jobListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeGlobalJobListener(String name)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeJobListener(String name) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public List getGlobalJobListeners() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public Set getJobListenerNames() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public JobListener getGlobalJobListener(String name) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public JobListener getJobListener(String name) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addGlobalTriggerListener(TriggerListener triggerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addTriggerListener(TriggerListener triggerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * @deprecated Use {@link #removeGlobalTriggerListener(String)}
+ */
+ public boolean removeGlobalTriggerListener(TriggerListener triggerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeGlobalTriggerListener(String name)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeTriggerListener(String name) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public List getGlobalTriggerListeners() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public Set getTriggerListenerNames() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public TriggerListener getGlobalTriggerListener(String name)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public TriggerListener getTriggerListener(String name)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public void addSchedulerListener(SchedulerListener schedulerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public boolean removeSchedulerListener(SchedulerListener schedulerListener)
+ throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ */
+ public List getSchedulerListeners() throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Scheduler#getPausedTriggerGroups()
+ */
+ public Set getPausedTriggerGroups() throws SchedulerException {
+ try {
+ return getRemoteScheduler().getPausedTriggerGroups(schedCtxt);
+ } catch (RemoteException re) {
+ throw invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re);
+ }
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Scheduler#interrupt(java.lang.String, java.lang.String)
+ */
+ public boolean interrupt(String jobName, String groupName) throws UnableToInterruptJobException {
+ try {
+ return getRemoteScheduler().interrupt(schedCtxt, jobName, groupName);
+ } catch (RemoteException re) {
+ throw new UnableToInterruptJobException(invalidateHandleCreateException(
+ "Error communicating with remote scheduler.", re));
+ } catch (SchedulerException se) {
+ throw new UnableToInterruptJobException(se);
+ }
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Scheduler#setJobFactory(com.fr.third.org.quartz.spi.JobFactory)
+ */
+ public void setJobFactory(JobFactory factory) throws SchedulerException {
+ throw new SchedulerException(
+ "Operation not supported for remote schedulers.",
+ SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION);
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/SchedulerRepository.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/SchedulerRepository.java
new file mode 100644
index 000000000..5c4406a58
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/SchedulerRepository.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerException;
+
+/**
+ *
+ * Holds references to Scheduler instances - ensuring uniqueness, and
+ * preventing garbage collection, and allowing 'global' lookups - all within a
+ * ClassLoader space.
+ *
+ *
+ * @author James House
+ */
+public class SchedulerRepository {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private HashMap schedulers;
+
+ private static SchedulerRepository inst;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private SchedulerRepository() {
+ schedulers = new HashMap();
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public static synchronized SchedulerRepository getInstance() {
+ if (inst == null) {
+ inst = new SchedulerRepository();
+ }
+
+ return inst;
+ }
+
+ public synchronized void bind(Scheduler sched) throws SchedulerException {
+
+ if ((Scheduler) schedulers.get(sched.getSchedulerName()) != null) {
+ throw new SchedulerException("Scheduler with name '"
+ + sched.getSchedulerName() + "' already exists.",
+ SchedulerException.ERR_BAD_CONFIGURATION);
+ }
+
+ schedulers.put(sched.getSchedulerName(), sched);
+ }
+
+ public synchronized boolean remove(String schedName) {
+ return (schedulers.remove(schedName) != null);
+ }
+
+ public synchronized Scheduler lookup(String schedName) {
+ return (Scheduler) schedulers.get(schedName);
+ }
+
+ public synchronized Collection lookupAll() {
+ return java.util.Collections
+ .unmodifiableCollection(schedulers.values());
+ }
+
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/StdJobRunShellFactory.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/StdJobRunShellFactory.java
new file mode 100644
index 000000000..ff50e97c6
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/StdJobRunShellFactory.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl;
+
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.core.JobRunShell;
+import com.fr.third.org.quartz.core.JobRunShellFactory;
+import com.fr.third.org.quartz.core.SchedulingContext;
+
+/**
+ *
+ * Responsible for creating the instances of {@link com.fr.third.org.quartz.core.JobRunShell}
+ * to be used within the {@link com.fr.third.org.quartz.core.QuartzScheduler}
+ *
instance.
+ *
+ *
+ *
+ * This implementation does not re-use any objects, it simply makes a new
+ * JobRunShell each time borrowJobRunShell()
is called.
+ *
+ * Initialize the factory, providing a handle to the Scheduler
+ * that should be made available within the JobRunShell
and
+ * the JobExecutionCOntext
s within it, and a handle to the
+ * SchedulingContext
that the shell will use in its own
+ * operations with the JobStore
.
+ *
+ * Called by the
+ * {@link com.fr.third.org.quartz.core.JobRunShell}
.
+ *
+ * Called by the
+ * {@link com.fr.third.org.quartz.core.JobRunShell}
.
+ *
+ * An implementation of the Scheduler
interface that directly
+ * proxies all method calls to the equivalent call on a given QuartzScheduler
+ * instance.
+ *
+ * Construct a StdScheduler
instance to proxy the given
+ * QuartzScheduler
instance, and with the given SchedulingContext
.
+ *
+ * Returns the name of the Scheduler
.
+ *
+ * Returns the instance Id of the Scheduler
.
+ *
+ * Returns the SchedulerContext
of the Scheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Note: This only reflects whether {@link #start()}
has ever
+ * been called on this Scheduler, so it will return true
even
+ * if the Scheduler
is currently in standby mode or has been
+ * since shutdown.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
,
+ * passing the SchedulingContext
associated with this
+ * instance.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
{@link #removeGlobalJobListener(String)}
+ */
+ public boolean removeGlobalJobListener(JobListener jobListener) {
+ return sched.removeGlobalJobListener(
+ (jobListener == null) ? null : jobListener.getName());
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
{@link #removeGlobalTriggerListener(String)}
+ */
+ public boolean removeGlobalTriggerListener(TriggerListener triggerListener) {
+ return sched.removeGlobalTriggerListener(
+ (triggerListener == null) ? null : triggerListener.getName());
+ }
+
+ /**
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * Calls the equivalent method on the 'proxied' QuartzScheduler
.
+ *
+ * An implementation of {@link com.fr.third.org.quartz.SchedulerFactory}
that
+ * does all of its work of creating a QuartzScheduler
instance
+ * based on the contenents of a Properties
file.
+ *
+ * By default a properties file named "quartz.properties" is loaded from the + * 'current working directory'. If that fails, then the "quartz.properties" + * file located (as a resource) in the org/quartz package is loaded. If you + * wish to use a file other than these defaults, you must define the system + * property 'com.fr.third.org.quartz.properties' to point to the file you want. + *
+ * + *+ * See the sample properties files that are distributed with Quartz for + * information about the various settings available within the file. + *
+ * + *
+ * Alternatively, you can explicitly initialize the factory by calling one of
+ * the initialize(xx)
methods before calling getScheduler()
.
+ *
+ * Instances of the specified {@link com.fr.third.org.quartz.spi.JobStore}
,
+ * {@link com.fr.third.org.quartz.spi.ThreadPool}
, classes will be created
+ * by name, and then any additional properties specified for them in the config
+ * file will be set on the instance by calling an equivalent 'set' method. For
+ * example if the properties file contains the property
+ * 'com.fr.third.org.quartz.jobStore.myProp = 10' then after the JobStore class has been
+ * instantiated, the method 'setMyProp()' will be called on it. Type conversion
+ * to primitive Java types (int, long, float, double, boolean, and String) are
+ * performed before calling the property's setter method.
+ *
{@link #initialize(Properties)}
.
+ *
+ * @see #initialize(Properties)
+ */
+ public StdSchedulerFactory(Properties props) throws SchedulerException {
+ initialize(props);
+ }
+
+ /**
+ * Create a StdSchedulerFactory that has been initialized via
+ * {@link #initialize(String)}
.
+ *
+ * @see #initialize(String)
+ */
+ public StdSchedulerFactory(String fileName) throws SchedulerException {
+ initialize(fileName);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public Log getLog() {
+ return log;
+ }
+
+ /**
+ *
+ * Initialize the {@link com.fr.third.org.quartz.SchedulerFactory}
with
+ * the contents of a Properties
file and overriding System
+ * properties.
+ *
+ * By default a properties file named "quartz.properties" is loaded from + * the 'current working directory'. If that fails, then the + * "quartz.properties" file located (as a resource) in the org/quartz + * package is loaded. If you wish to use a file other than these defaults, + * you must define the system property 'com.fr.third.org.quartz.properties' to point to + * the file you want. + *
+ * + *
+ * System properties (environment variables, and -D definitions on the
+ * command-line when running the JVM) override any properties in the
+ * loaded file. For this reason, you may want to use a different initialize()
+ * method if your application security policy prohibits access to
+ * {@link java.lang.System#getProperties()}
.
+ *
props
. Will override
+ * any properties that already exist in the given props
.
+ */
+ private Properties overrideWithSysProps(Properties props) {
+ Properties sysProps = null;
+ try {
+ sysProps = System.getProperties();
+ } catch (AccessControlException e) {
+ getLog().warn(
+ "Skipping overriding quartz properties with System properties " +
+ "during initialization because of an AccessControlException. " +
+ "This is likely due to not having read/write access for " +
+ "java.util.PropertyPermission as required by java.lang.System.getProperties(). " +
+ "To resolve this warning, either add this permission to your policy file or " +
+ "use a non-default version of initialize().",
+ e);
+ }
+
+ if (sysProps != null) {
+ props.putAll(sysProps);
+ }
+
+ return props;
+ }
+
+ /**
+ *
+ * Initialize the {@link com.fr.third.org.quartz.SchedulerFactory}
with
+ * the contenents of the Properties
file with the given
+ * name.
+ *
+ * Initialize the {@link com.fr.third.org.quartz.SchedulerFactory}
with
+ * the contenents of the Properties
file opened with the
+ * given InputStream
.
+ *
+ * Initialize the {@link com.fr.third.org.quartz.SchedulerFactory}
with
+ * the contenents of the given Properties
object.
+ *
+ * Returns a handle to the Scheduler produced by this factory. + *
+ * + *
+ * If one of the initialize
methods has not be previously
+ * called, then the default (no-arg) initialize()
method
+ * will be called by this method.
+ *
+ * Returns a handle to the default Scheduler, creating it if it does not + * yet exist. + *
+ * + * @see #initialize() + */ + public static Scheduler getDefaultScheduler() throws SchedulerException { + StdSchedulerFactory fact = new StdSchedulerFactory(); + + return fact.getScheduler(); + } + + /** + *+ * Returns a handle to the Scheduler with the given name, if it exists (if + * it has already been instantiated). + *
+ */ + public Scheduler getScheduler(String schedName) throws SchedulerException { + return SchedulerRepository.getInstance().lookup(schedName); + } + + /** + *+ * Returns a handle to all known Schedulers (made by any + * StdSchedulerFactory instance.). + *
+ */ + public Collection getAllSchedulers() throws SchedulerException { + return SchedulerRepository.getInstance().lookupAll(); + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/AnnualCalendar.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/AnnualCalendar.java new file mode 100644 index 000000000..e703402fe --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/AnnualCalendar.java @@ -0,0 +1,294 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + * and Juergen Donnerstag (c) 2002, EDS 2002 + */ + +package com.fr.third.org.quartz.impl.calendar; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.TimeZone; + +import com.fr.third.org.quartz.Calendar; + +/** + *+ * This implementation of the Calendar excludes a set of days of the year. You + * may use it to exclude bank holidays which are on the same date every year. + *
+ * + * @see com.fr.third.org.quartz.Calendar + * @see com.fr.third.org.quartz.impl.calendar.BaseCalendar + * + * @author Juergen Donnerstag + */ +public class AnnualCalendar extends BaseCalendar implements Calendar, + Serializable { + + static final long serialVersionUID = 7346867105876610961L; + + private ArrayList excludeDays = new ArrayList(); + + // true, if excludeDays is sorted + private boolean dataSorted = false; + + public AnnualCalendar() { + } + + public AnnualCalendar(Calendar baseCalendar) { + super(baseCalendar); + } + + public AnnualCalendar(TimeZone timeZone) { + super(timeZone); + } + + public AnnualCalendar(Calendar baseCalendar, TimeZone timeZone) { + super(baseCalendar, timeZone); + } + + /** + *+ * Get the array which defines the exclude-value of each day of month + *
+ */ + public ArrayList getDaysExcluded() { + return excludeDays; + } + + /** + *+ * Return true, if day is defined to be exluded. + *
+ */ + public boolean isDayExcluded(java.util.Calendar day) { + + if (day == null) { + throw new IllegalArgumentException( + "Parameter day must not be null"); + } + + // Check baseCalendar first + if (! super.isTimeIncluded(day.getTime().getTime())) { + return true; + } + + int dmonth = day.get(java.util.Calendar.MONTH); + int dday = day.get(java.util.Calendar.DAY_OF_MONTH); + + if (dataSorted == false) { + Collections.sort(excludeDays, new CalendarComparator()); + dataSorted = true; + } + + Iterator iter = excludeDays.iterator(); + while (iter.hasNext()) { + java.util.Calendar cl = (java.util.Calendar) iter.next(); + + // remember, the list is sorted + if (dmonth < cl.get(java.util.Calendar.MONTH)) { + return false; + } + + if (dday != cl.get(java.util.Calendar.DAY_OF_MONTH)) { + continue; + } + + if (dmonth != cl.get(java.util.Calendar.MONTH)) { + continue; + } + + return true; + } + + return false; + } + + /** + *
+ * Redefine the list of days excluded. The ArrayList
+ * should contain java.util.Calendar
objects.
+ *
+ * Redefine a certain day to be excluded (true) or included (false). + *
+ */ + public void setDayExcluded(java.util.Calendar day, boolean exclude) { + if (exclude) { + if (isDayExcluded(day)) { + return; + } + + excludeDays.add(day); + dataSorted = false; + } else { + if (!isDayExcluded(day)) { + return; + } + + removeExcludedDay(day, true); + } + } + + /** + * Remove the given day from the list of excluded days + * + * @param day + * @return + */ + public void removeExcludedDay(java.util.Calendar day) { + removeExcludedDay(day, false); + } + + private void removeExcludedDay(java.util.Calendar day, boolean isChecked) { + if (! isChecked && + ! isDayExcluded(day)) { + return; + } + + // Fast way, see if exact day object was already in list + if (this.excludeDays.remove(day)) { + return; + } + + int dmonth = day.get(java.util.Calendar.MONTH); + int dday = day.get(java.util.Calendar.DAY_OF_MONTH); + + // Since there is no guarantee that the given day is in the arraylist with the exact same year + // search for the object based on month and day of month in the list and remove it + Iterator iter = excludeDays.iterator(); + while (iter.hasNext()) { + java.util.Calendar cl = (java.util.Calendar) iter.next(); + + if (dmonth != cl.get(java.util.Calendar.MONTH)) { + continue; + } + + if (dday != cl.get(java.util.Calendar.DAY_OF_MONTH)) { + continue; + } + + day = cl; + break; + } + + this.excludeDays.remove(day); + } + + + /** + *+ * Determine whether the given time (in milliseconds) is 'included' by the + * Calendar. + *
+ * + *+ * Note that this Calendar is only has full-day precision. + *
+ */ + public boolean isTimeIncluded(long timeStamp) { + // Test the base calendar first. Only if the base calendar not already + // excludes the time/date, continue evaluating this calendar instance. + if (super.isTimeIncluded(timeStamp) == false) { return false; } + + java.util.Calendar day = createJavaCalendar(timeStamp); + + return !(isDayExcluded(day)); + } + + /** + *+ * Determine the next time (in milliseconds) that is 'included' by the + * Calendar after the given time. Return the original value if timeStamp is + * included. Return 0 if all days are excluded. + *
+ * + *+ * Note that this Calendar is only has full-day precision. + *
+ */ + public long getNextIncludedTime(long timeStamp) { + // Call base calendar implementation first + long baseTime = super.getNextIncludedTime(timeStamp); + if ((baseTime > 0) && (baseTime > timeStamp)) { + timeStamp = baseTime; + } + + // Get timestamp for 00:00:00 + java.util.Calendar day = getStartOfDayJavaCalendar(timeStamp); + if (isDayExcluded(day) == false) { + return timeStamp; // return the original value + } + + while (isDayExcluded(day) == true) { + day.add(java.util.Calendar.DATE, 1); + } + + return day.getTime().getTime(); + } +} + +class CalendarComparator implements Comparator { + public CalendarComparator() { + } + + /** + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + public int compare(Object arg0, Object arg1) { + java.util.Calendar c1 = (java.util.Calendar) arg0; + java.util.Calendar c2 = (java.util.Calendar) arg1; + + int month1 = c1.get(java.util.Calendar.MONTH); + int month2 = c2.get(java.util.Calendar.MONTH); + + int day1 = c1.get(java.util.Calendar.DAY_OF_MONTH); + int day2 = c2.get(java.util.Calendar.DAY_OF_MONTH); + + if (month1 < month2) { + return -1; + } + if (month1 > month2) { + return 1; + } + if (day1 < day2) { + return -1; + } + if (day1 > day2) { + return 1; + } + return 0; + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/BaseCalendar.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/BaseCalendar.java new file mode 100644 index 000000000..2e315231e --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/BaseCalendar.java @@ -0,0 +1,283 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + * and Juergen Donnerstag (c) 2002, EDS 2002 + */ + +package com.fr.third.org.quartz.impl.calendar; + +import java.io.Serializable; +import java.util.Date; +import java.util.TimeZone; + +import com.fr.third.org.quartz.Calendar; + +/** + *+ * This implementation of the Calendar may be used (you don't have to) as a + * base class for more sophisticated one's. It merely implements the base + * functionality required by each Calendar. + *
+ * + *+ * Regarded as base functionality is the treatment of base calendars. Base + * calendar allow you to chain (stack) as much calendars as you may need. For + * example to exclude weekends you may use WeeklyCalendar. In order to exclude + * holidays as well you may define a WeeklyCalendar instance to be the base + * calendar for HolidayCalendar instance. + *
+ * + * @see com.fr.third.org.quartz.Calendar + * + * @author Juergen Donnerstag + * @author James House + */ +public class BaseCalendar implements Calendar, Serializable { + + static final long serialVersionUID = 3106623404629760239L; + + //A optional base calendar.
+ private Calendar baseCalendar; + + private String description; + + private TimeZone timeZone; + + public BaseCalendar() { + } + + public BaseCalendar(Calendar baseCalendar) { + setBaseCalendar(baseCalendar); + } + + /** + * @param timeZone The time zone to use for this Calendar,null
+ * if {@link TimeZone#getDefault()}
should be used
+ */
+ public BaseCalendar(TimeZone timeZone) {
+ setTimeZone(timeZone);
+ }
+
+ /**
+ * @param timeZone The time zone to use for this Calendar, null
+ * if {@link TimeZone#getDefault()}
should be used
+ */
+ public BaseCalendar(Calendar baseCalendar, TimeZone timeZone) {
+ setBaseCalendar(baseCalendar);
+ setTimeZone(timeZone);
+ }
+
+ /**
+ * + * Set a new base calendar or remove the existing one + *
+ */ + public void setBaseCalendar(Calendar baseCalendar) { + this.baseCalendar = baseCalendar; + } + + /** + *+ * Get the base calendar. Will be null, if not set. + *
+ */ + public Calendar getBaseCalendar() { + return this.baseCalendar; + } + + /** + *
+ * Return the description given to the Calendar
instance by
+ * its creator (if any).
+ *
+ * Set a description for the Calendar
instance - may be
+ * useful for remembering/displaying the purpose of the calendar, though
+ * the description has no meaning to Quartz.
+ *
Calendar
will be
+ * resolved.
+ *
+ * @return This Calendar's timezone, null
if Calendar should
+ * use the {@link TimeZone#getDefault()}
+ */
+ public TimeZone getTimeZone() {
+ return timeZone;
+ }
+
+ /**
+ * Sets the time zone for which this Calendar
will be resolved.
+ *
+ * @param timeZone The time zone to use for this Calendar, null
+ * if {@link TimeZone#getDefault()}
should be used
+ */
+ public void setTimeZone(TimeZone timeZone) {
+ this.timeZone = timeZone;
+ }
+
+ /**
+ * + * Check if date/time represented by timeStamp is included. If included + * return true. The implementation of BaseCalendar simply calls the base + * calendars isTimeIncluded() method if base calendar is set. + *
+ * + * @see com.fr.third.org.quartz.Calendar#isTimeIncluded(long) + */ + public boolean isTimeIncluded(long timeStamp) { + + if (timeStamp <= 0) { + throw new IllegalArgumentException( + "timeStamp must be greater 0"); + } + + if (baseCalendar != null) { + if (baseCalendar.isTimeIncluded(timeStamp) == false) { return false; } + } + + return true; + } + + /** + *+ * Determine the next time (in milliseconds) that is 'included' by the + * Calendar after the given time. Return the original value if timeStamp is + * included. Return 0 if all days are excluded. + *
+ * + * @see com.fr.third.org.quartz.Calendar#getNextIncludedTime(long) + */ + public long getNextIncludedTime(long timeStamp) { + + if (timeStamp <= 0) { + throw new IllegalArgumentException( + "timeStamp must be greater 0"); + } + + if (baseCalendar != null) { + return baseCalendar.getNextIncludedTime(timeStamp); + } + + return timeStamp; + } + + /** + * Utility method. Return the date of excludeDate. The time fraction will + * be reset to 00.00:00. + * + * @deprecated Always uses the default time zone. + */ + public static Date buildHoliday(Date excludedDate) { + return new BaseCalendar().getStartOfDayJavaCalendar(excludedDate.getTime()).getTime(); + } + + /** + * Utility method. Return just the date of timeStamp. The time fraction + * will be reset to 00.00:00. + * + * @deprecated Always uses the default time zone. + */ + public static long buildHoliday(long timeStamp) { + return new BaseCalendar().getStartOfDayJavaCalendar(timeStamp).getTime().getTime(); + } + + /** + * Utility method. Return a java.util.Calendar for timeStamp. + * + * @deprecated Always uses the default time zone. + */ + public static java.util.Calendar getJavaCalendar(long timeStamp) { + return new BaseCalendar().createJavaCalendar(timeStamp); + } + + /** + * Build a{@link java.util.Calendar}
for the given timeStamp.
+ * The new Calendar will use the BaseCalendar
time zone if it
+ * is not null
.
+ */
+ protected java.util.Calendar createJavaCalendar(long timeStamp) {
+ java.util.Calendar calendar = createJavaCalendar();
+ calendar.setTime(new Date(timeStamp));
+ return calendar;
+ }
+
+ /**
+ * Build a {@link java.util.Calendar}
with the current time.
+ * The new Calendar will use the BaseCalendar
time zone if
+ * it is not null
.
+ */
+ protected java.util.Calendar createJavaCalendar() {
+ return
+ (getTimeZone() == null) ?
+ java.util.Calendar.getInstance() :
+ java.util.Calendar.getInstance(getTimeZone());
+ }
+
+ /**
+ * Returns the start of the given day as a {@link java.util.Calendar}
.
+ * This calculation will take the BaseCalendar
+ * time zone into account if it is not null
.
+ *
+ * @param timeInMillis A time containing the desired date for the
+ * start-of-day time
+ * @return A {@link java.util.Calendar}
set to the start of
+ * the given day.
+ */
+ protected java.util.Calendar getStartOfDayJavaCalendar(long timeInMillis) {
+ java.util.Calendar startOfDay = createJavaCalendar(timeInMillis);
+ startOfDay.set(java.util.Calendar.HOUR_OF_DAY, 0);
+ startOfDay.set(java.util.Calendar.MINUTE, 0);
+ startOfDay.set(java.util.Calendar.SECOND, 0);
+ startOfDay.set(java.util.Calendar.MILLISECOND, 0);
+ return startOfDay;
+ }
+
+ /**
+ * Returns the end of the given day {@link java.util.Calendar}
.
+ * This calculation will take the BaseCalendar
+ * time zone into account if it is not null
.
+ *
+ * @param timeInMillis a time containing the desired date for the
+ * end-of-day time.
+ * @return A {@link java.util.Calendar}
set to the end of
+ * the given day.
+ */
+ protected java.util.Calendar getEndOfDayJavaCalendar(long timeInMillis) {
+ java.util.Calendar endOfDay = createJavaCalendar(timeInMillis);
+ endOfDay.set(java.util.Calendar.HOUR_OF_DAY, 23);
+ endOfDay.set(java.util.Calendar.MINUTE, 59);
+ endOfDay.set(java.util.Calendar.SECOND, 59);
+ endOfDay.set(java.util.Calendar.MILLISECOND, 999);
+ return endOfDay;
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/CronCalendar.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/CronCalendar.java
new file mode 100644
index 000000000..8ddf8ce83
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/CronCalendar.java
@@ -0,0 +1,262 @@
+package com.fr.third.org.quartz.impl.calendar;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.TimeZone;
+
+import com.fr.third.org.quartz.Calendar;
+import com.fr.third.org.quartz.CronExpression;
+
+/**
+ * This implementation of the Calendar excludes the set of times expressed by a
+ * given {@link com.fr.third.org.quartz.CronExpression CronExpression}. For example, you
+ * could use this calendar to exclude all but business hours (8AM - 5PM) every
+ * day using the expression "* * 0-7,18-23 ? * *".
+ *
+ * It is important to remember that the cron expression here describes a set of
+ * times to be excluded from firing. Whereas the cron expression in
+ * {@link com.fr.third.org.quartz.CronTrigger CronTrigger} describes a set of times that can
+ * be included for firing. Thus, if a CronTrigger
has a
+ * given cron expression and is associated with a CronCalendar
with
+ * the same expression, the calendar will exclude all the times the
+ * trigger includes, and they will cancel each other out.
+ *
+ * @author Aaron Craven
+ */
+public class CronCalendar extends BaseCalendar {
+ static final long serialVersionUID = -8172103999750856831L;
+
+ /** @deprecated The use of name
is no longer supported. */
+ private String name;
+
+ CronExpression cronExpression;
+
+ /**
+ * Create a CronCalendar
with the given cron expression and no
+ * baseCalendar
.
+ *
+ * @param expression a String representation of the desired cron expression
+ */
+ public CronCalendar(String expression)
+ throws ParseException {
+ this(null, expression, null);
+ }
+
+ /**
+ * Create a CronCalendar
with the given cron expression and
+ * baseCalendar
.
+ *
+ * @param baseCalendar the base calendar for this calendar instance –
+ * see {@link BaseCalendar} for more information on base
+ * calendar functionality
+ * @param expression a String representation of the desired cron expression
+ */
+ public CronCalendar(Calendar baseCalendar,
+ String expression) throws ParseException {
+ this(baseCalendar, expression, null);
+ }
+
+ /**
+ * Create a CronCalendar
with the given cron exprssion,
+ * baseCalendar
, and TimeZone
.
+ *
+ * @param baseCalendar the base calendar for this calendar instance –
+ * see {@link BaseCalendar} for more information on base
+ * calendar functionality
+ * @param expression a String representation of the desired cron expression
+ * @param timeZone
+ * Specifies for which time zone the expression
+ * should be interpreted, i.e. the expression 0 0 10 * * ?, is
+ * resolved to 10:00 am in this time zone. If
+ * timeZone
is null
then
+ * TimeZone.getDefault()
will be used.
+ */
+ public CronCalendar(Calendar baseCalendar,
+ String expression, TimeZone timeZone) throws ParseException {
+ super(baseCalendar);
+ this.cronExpression = new CronExpression(expression);
+ this.cronExpression.setTimeZone(timeZone);
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see #CronCalendar(String)
+ */
+ public CronCalendar(String name, String expression)
+ throws ParseException {
+ this(expression);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see #CronCalendar(Calendar, String)
+ */
+ public CronCalendar(String name, Calendar baseCalendar,
+ String expression) throws ParseException {
+ this(baseCalendar, expression);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see #CronCalendar(Calendar, String, TimeZone)
+ */
+ public CronCalendar(String name, Calendar baseCalendar,
+ String expression, TimeZone timeZone) throws ParseException {
+ this(baseCalendar, expression, timeZone);
+ this.name = name;
+ }
+
+ /**
+ * Returns the time zone for which the CronExpression
of
+ * this CronCalendar
will be resolved.
+ *
+ * Overrides {@link BaseCalendar#getTimeZone()}
to
+ * defer to its CronExpression
.
+ *
CronExpression
of this
+ * CronCalendar
will be resolved. If timeZone
+ * is null
then TimeZone.getDefault()
will be
+ * used.
+ *
+ * Overrides {@link BaseCalendar#setTimeZone(TimeZone)}
to
+ * defer to its CronExpression
.
+ *
CronCalendar
+ *
+ * @return the name of the CronCalendar
+ *
+ * @deprecated The use of name
is no longer supported.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Determines whether the given time (in milliseconds) is 'included' by the
+ * BaseCalendar
+ *
+ * @param timeInMillis the date/time to test
+ * @return a boolean indicating whether the specified time is 'included' by
+ * the CronCalendar
+ */
+ public boolean isTimeIncluded(long timeInMillis) {
+ if ((getBaseCalendar() != null) &&
+ (getBaseCalendar().isTimeIncluded(timeInMillis) == false)) {
+ return false;
+ }
+
+ return (!(cronExpression.isSatisfiedBy(new Date(timeInMillis))));
+ }
+
+ /**
+ * Determines the next time included by the CronCalendar
+ * after the specified time.
+ *
+ * @param timeInMillis the initial date/time after which to find an
+ * included time
+ * @return the time in milliseconds representing the next time included
+ * after the specified time.
+ */
+ public long getNextIncludedTime(long timeInMillis) {
+ long nextIncludedTime = timeInMillis + 1; //plus on millisecond
+
+ while (!isTimeIncluded(nextIncludedTime)) {
+
+ //If the time is in a range excluded by this calendar, we can
+ // move to the end of the excluded time range and continue testing
+ // from there. Otherwise, if nextIncludedTime is excluded by the
+ // baseCalendar, ask it the next time it includes and begin testing
+ // from there. Failing this, add one millisecond and continue
+ // testing.
+ if (cronExpression.isSatisfiedBy(new Date(nextIncludedTime))) {
+ nextIncludedTime = cronExpression.getNextInvalidTimeAfter(
+ new Date(nextIncludedTime)).getTime();
+ } else if ((getBaseCalendar() != null) &&
+ (!getBaseCalendar().isTimeIncluded(nextIncludedTime))){
+ nextIncludedTime =
+ getBaseCalendar().getNextIncludedTime(nextIncludedTime);
+ } else {
+ nextIncludedTime++;
+ }
+ }
+
+ return nextIncludedTime;
+ }
+
+ /**
+ * Returns a string representing the properties of the
+ * CronCalendar
+ *
+ * @return the properteis of the CronCalendar in a String format
+ */
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ if (name != null) {
+ buffer.append(name).append(": ");
+ }
+ buffer.append("base calendar: [");
+ if (getBaseCalendar() != null) {
+ buffer.append(getBaseCalendar().toString());
+ } else {
+ buffer.append("null");
+ }
+ buffer.append("], excluded cron expression: '");
+ buffer.append(cronExpression);
+ buffer.append("'");
+ return buffer.toString();
+ }
+
+ /**
+ * Returns the object representation of the cron expression that defines the
+ * dates and times this calendar excludes.
+ *
+ * @return the cron expression
+ * @see com.fr.third.org.quartz.CronExpression
+ */
+ public CronExpression getCronExpression() {
+ return cronExpression;
+ }
+
+ /**
+ * Sets the cron expression for the calendar to a new value
+ *
+ * @param expression the new string value to build a cron expression from
+ * @throws ParseException
+ * if the string expression cannot be parsed
+ */
+ public void setCronExpression(String expression) throws ParseException {
+ CronExpression newExp = new CronExpression(expression);
+
+ this.cronExpression = newExp;
+ }
+
+ /**
+ * Sets the cron expression for the calendar to a new value
+ *
+ * @param expression the new cron expression
+ */
+ public void setCronExpression(CronExpression expression) {
+ if (expression == null) {
+ throw new IllegalArgumentException("expression cannot be null");
+ }
+
+ this.cronExpression = expression;
+ }
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/DailyCalendar.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/DailyCalendar.java
new file mode 100644
index 000000000..496f26915
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/DailyCalendar.java
@@ -0,0 +1,1034 @@
+package com.fr.third.org.quartz.impl.calendar;
+
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+
+/**
+ * This implementation of the Calendar excludes (or includes - see below) a
+ * specified time range each day. For example, you could use this calendar to
+ * exclude business hours (8AM - 5PM) every day. Each DailyCalendar
+ * only allows a single time range to be specified, and that time range may not
+ * cross daily boundaries (i.e. you cannot specify a time range from 8PM - 5AM).
+ * If the property invertTimeRange
is false
(default),
+ * the time range defines a range of times in which triggers are not allowed to
+ * fire. If invertTimeRange
is true
, the time range
+ * is inverted – that is, all times outside the defined time range
+ * are excluded.
+ *
+ * Note when using DailyCalendar
, it behaves on the same principals
+ * as, for example, {@link com.fr.third.org.quartz.impl.calendar.WeeklyCalendar
+ * WeeklyCalendar}. WeeklyCalendar
defines a set of days that are
+ * excluded every week. Likewise, DailyCalendar
defines a
+ * set of times that are excluded every day.
+ *
+ * @author Mike Funk, Aaron Craven
+ */
+public class DailyCalendar extends BaseCalendar {
+ static final long serialVersionUID = -7561220099904944039L;
+
+ private static final String invalidHourOfDay = "Invalid hour of day: ";
+ private static final String invalidMinute = "Invalid minute: ";
+ private static final String invalidSecond = "Invalid second: ";
+ private static final String invalidMillis = "Invalid millis: ";
+ private static final String invalidTimeRange = "Invalid time range: ";
+ private static final String separator = " - ";
+ private static final long oneMillis = 1;
+ private static final String colon = ":";
+
+ /** @deprecated The use of name
is no longer supported. */
+ private String name;
+
+ private int rangeStartingHourOfDay;
+ private int rangeStartingMinute;
+ private int rangeStartingSecond;
+ private int rangeStartingMillis;
+ private int rangeEndingHourOfDay;
+ private int rangeEndingMinute;
+ private int rangeEndingSecond;
+ private int rangeEndingMillis;
+
+ private boolean invertTimeRange = false;
+
+ /**
+ * Create a DailyCalendar
with a time range defined by the
+ * specified strings and no baseCalendar
.
+ * rangeStartingTime
and rangeEndingTime
+ * must be in the format "HH:MM[:SS[:mmm]]" where:
+ *
+ * Note: This DailyCalendar
will use the
+ * {@link TimeZone#getDefault()}
time zone unless an explicit
+ * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)}
+ *
DailyCalendar
with a time range defined by the
+ * specified strings and the specified baseCalendar
.
+ * rangeStartingTime
and rangeEndingTime
+ * must be in the format "HH:MM[:SS[:mmm]]" where:
+ *
+ * Note: This DailyCalendar
will use the
+ * {@link TimeZone#getDefault()}
time zone unless an explicit
+ * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)}
+ *
DailyCalendar
with a time range defined by the
+ * specified values and no baseCalendar
. Values are subject to
+ * the following validations:
+ *
+ * Note: This DailyCalendar
will use the
+ * {@link TimeZone#getDefault()}
time zone unless an explicit
+ * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)}
+ *
DailyCalendar
with a time range defined by the
+ * specified values and the specified baseCalendar
. Values are
+ * subject to the following validations:
+ *
+ * Note: This DailyCalendar
will use the
+ * {@link TimeZone#getDefault()}
time zone unless an explicit
+ * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)}
+ *
DailyCalendar
with a time range defined by the
+ * specified java.util.Calendar
s and no
+ * baseCalendar
. The Calendars are subject to the following
+ * considerations:
+ * rangeStartingCalendar.after(rangeEndingCalendar) ==
+ * true
)
+ * Note: This DailyCalendar
will use the
+ * {@link TimeZone#getDefault()}
time zone unless an explicit
+ * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)}
+ *
DailyCalendar
with a time range defined by the
+ * specified java.util.Calendar
s and the specified
+ * baseCalendar
. The Calendars are subject to the following
+ * considerations:
+ * rangeStartingCalendar.after(rangeEndingCalendar) ==
+ * true
)
+ * Note: This DailyCalendar
will use the
+ * {@link TimeZone#getDefault()}
time zone unless an explicit
+ * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)}
+ *
DailyCalendar
with a time range defined by the
+ * specified values and no baseCalendar
. The values are
+ * subject to the following considerations:
+ * rangeStartingTime >
+ * rangeEndingTime
)
+ * Note: This DailyCalendar
will use the
+ * {@link TimeZone#getDefault()}
time zone unless an explicit
+ * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)}
.
+ * You should use {@link #DailyCalendar(String, TimeZone, long, long)}
+ * if you don't want the given rangeStartingTimeInMillis
and
+ * rangeEndingTimeInMillis
to be evaluated in the default
+ * time zone.
+ *
DailyCalendar
with a time range defined by the
+ * specified values and the specified baseCalendar
. The values
+ * are subject to the following considerations:
+ * rangeStartingTime >
+ * rangeEndingTime
)
+ * Note: This DailyCalendar
will use the
+ * {@link TimeZone#getDefault()}
time zone unless an explicit
+ * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)}
.
+ * You should use {@link #DailyCalendar(String, Calendar, TimeZone, long, long)}
+ * if you don't want the given rangeStartingTimeInMillis
and
+ * rangeEndingTimeInMillis
to be evaluated in the default
+ * time zone.
+ *
DailyCalendar
with a time range defined by the
+ * specified values and no baseCalendar
. The values are
+ * subject to the following considerations:
+ * rangeStartingTime >
+ * rangeEndingTime
)DailyCalendar
which will
+ * also be used to resolve the given
+ * start/end times.
+ * @param rangeStartingTimeInMillis a long representing the starting time
+ * for the time range
+ * @param rangeEndingTimeInMillis a long representing the ending time for
+ * the time range
+ */
+ public DailyCalendar(TimeZone timeZone,
+ long rangeStartingTimeInMillis,
+ long rangeEndingTimeInMillis) {
+ super(timeZone);
+ setTimeRange(rangeStartingTimeInMillis,
+ rangeEndingTimeInMillis);
+ }
+
+ /**
+ * Create a DailyCalendar
with a time range defined by the
+ * specified values and the specified baseCalendar
. The values
+ * are subject to the following considerations:
+ * rangeStartingTime >
+ * rangeEndingTime
)DailyCalendar
which will
+ * also be used to resolve the given
+ * start/end times.
+ * @param rangeStartingTimeInMillis a long representing the starting time
+ * for the time range
+ * @param rangeEndingTimeInMillis a long representing the ending time for
+ * the time range
+ */
+ public DailyCalendar(com.fr.third.org.quartz.Calendar baseCalendar,
+ TimeZone timeZone,
+ long rangeStartingTimeInMillis,
+ long rangeEndingTimeInMillis) {
+ super(baseCalendar, timeZone);
+ setTimeRange(rangeStartingTimeInMillis,
+ rangeEndingTimeInMillis);
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(String, String)
+ */
+ public DailyCalendar(String name,
+ String rangeStartingTime,
+ String rangeEndingTime) {
+ this(rangeStartingTime, rangeEndingTime);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(com.fr.third.org.quartz.Calendar, String, String)
+ */
+ public DailyCalendar(String name,
+ com.fr.third.org.quartz.Calendar baseCalendar,
+ String rangeStartingTime,
+ String rangeEndingTime) {
+ this(baseCalendar, rangeStartingTime, rangeEndingTime);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(int, int, int, int, int, int, int, int)
+ */
+ public DailyCalendar(String name,
+ int rangeStartingHourOfDay,
+ int rangeStartingMinute,
+ int rangeStartingSecond,
+ int rangeStartingMillis,
+ int rangeEndingHourOfDay,
+ int rangeEndingMinute,
+ int rangeEndingSecond,
+ int rangeEndingMillis) {
+ this(rangeStartingHourOfDay,
+ rangeStartingMinute,
+ rangeStartingSecond,
+ rangeStartingMillis,
+ rangeEndingHourOfDay,
+ rangeEndingMinute,
+ rangeEndingSecond,
+ rangeEndingMillis);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(com.fr.third.org.quartz.Calendar, int, int, int, int, int, int, int, int)
+ */
+ public DailyCalendar(String name,
+ com.fr.third.org.quartz.Calendar baseCalendar,
+ int rangeStartingHourOfDay,
+ int rangeStartingMinute,
+ int rangeStartingSecond,
+ int rangeStartingMillis,
+ int rangeEndingHourOfDay,
+ int rangeEndingMinute,
+ int rangeEndingSecond,
+ int rangeEndingMillis) {
+ this(baseCalendar,
+ rangeStartingHourOfDay,
+ rangeStartingMinute,
+ rangeStartingSecond,
+ rangeStartingMillis,
+ rangeEndingHourOfDay,
+ rangeEndingMinute,
+ rangeEndingSecond,
+ rangeEndingMillis);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(Calendar, Calendar)
+ */
+ public DailyCalendar(String name,
+ Calendar rangeStartingCalendar,
+ Calendar rangeEndingCalendar) {
+ this(rangeStartingCalendar, rangeEndingCalendar);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(com.fr.third.org.quartz.Calendar, Calendar, Calendar)
+ */
+ public DailyCalendar(String name,
+ com.fr.third.org.quartz.Calendar baseCalendar,
+ Calendar rangeStartingCalendar,
+ Calendar rangeEndingCalendar) {
+ this(baseCalendar, rangeStartingCalendar, rangeEndingCalendar);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(long, long)
+ */
+ public DailyCalendar(String name,
+ long rangeStartingTimeInMillis,
+ long rangeEndingTimeInMillis) {
+ this(rangeStartingTimeInMillis, rangeEndingTimeInMillis);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(com.fr.third.org.quartz.Calendar, long, long)
+ */
+ public DailyCalendar(String name,
+ com.fr.third.org.quartz.Calendar baseCalendar,
+ long rangeStartingTimeInMillis,
+ long rangeEndingTimeInMillis) {
+ this(baseCalendar, rangeStartingTimeInMillis, rangeEndingTimeInMillis);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(TimeZone, long, long)
+ */
+ public DailyCalendar(String name,
+ TimeZone timeZone,
+ long rangeStartingTimeInMillis,
+ long rangeEndingTimeInMillis) {
+ this(timeZone,
+ rangeStartingTimeInMillis,
+ rangeEndingTimeInMillis);
+ this.name = name;
+ }
+
+ /**
+ * @deprecated The use of name
is no longer supported.
+ *
+ * @see DailyCalendar#DailyCalendar(com.fr.third.org.quartz.Calendar, TimeZone, long, long)
+ */
+ public DailyCalendar(String name,
+ com.fr.third.org.quartz.Calendar baseCalendar,
+ TimeZone timeZone,
+ long rangeStartingTimeInMillis,
+ long rangeEndingTimeInMillis) {
+ this(baseCalendar,
+ timeZone,
+ rangeStartingTimeInMillis,
+ rangeEndingTimeInMillis);
+ this.name = name;
+ }
+
+ /**
+ * Returns the name of the DailyCalendar
+ *
+ * @return the name of the DailyCalendar
+ *
+ * @deprecated The use of name
is no longer supported.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Determines whether the given time (in milliseconds) is 'included' by the
+ * BaseCalendar
+ *
+ * @param timeInMillis the date/time to test
+ * @return a boolean indicating whether the specified time is 'included' by
+ * the BaseCalendar
+ */
+ public boolean isTimeIncluded(long timeInMillis) {
+ if ((getBaseCalendar() != null) &&
+ (getBaseCalendar().isTimeIncluded(timeInMillis) == false)) {
+ return false;
+ }
+
+ long startOfDayInMillis = getStartOfDayJavaCalendar(timeInMillis).getTime().getTime();
+ long endOfDayInMillis = getEndOfDayJavaCalendar(timeInMillis).getTime().getTime();
+ long timeRangeStartingTimeInMillis =
+ getTimeRangeStartingTimeInMillis(timeInMillis);
+ long timeRangeEndingTimeInMillis =
+ getTimeRangeEndingTimeInMillis(timeInMillis);
+ if (!invertTimeRange) {
+ return
+ ((timeInMillis > startOfDayInMillis &&
+ timeInMillis < timeRangeStartingTimeInMillis) ||
+ (timeInMillis > timeRangeEndingTimeInMillis &&
+ timeInMillis < endOfDayInMillis));
+ } else {
+ return ((timeInMillis >= timeRangeStartingTimeInMillis) &&
+ (timeInMillis <= timeRangeEndingTimeInMillis));
+ }
+ }
+
+ /**
+ * Determines the next time included by the DailyCalendar
+ * after the specified time.
+ *
+ * @param timeInMillis the initial date/time after which to find an
+ * included time
+ * @return the time in milliseconds representing the next time included
+ * after the specified time.
+ */
+ public long getNextIncludedTime(long timeInMillis) {
+ long nextIncludedTime = timeInMillis + oneMillis;
+
+ while (!isTimeIncluded(nextIncludedTime)) {
+ if (!invertTimeRange) {
+ //If the time is in a range excluded by this calendar, we can
+ // move to the end of the excluded time range and continue
+ // testing from there. Otherwise, if nextIncludedTime is
+ // excluded by the baseCalendar, ask it the next time it
+ // includes and begin testing from there. Failing this, add one
+ // millisecond and continue testing.
+ if ((nextIncludedTime >=
+ getTimeRangeStartingTimeInMillis(nextIncludedTime)) &&
+ (nextIncludedTime <=
+ getTimeRangeEndingTimeInMillis(nextIncludedTime))) {
+
+ nextIncludedTime =
+ getTimeRangeEndingTimeInMillis(nextIncludedTime) +
+ oneMillis;
+ } else if ((getBaseCalendar() != null) &&
+ (!getBaseCalendar().isTimeIncluded(nextIncludedTime))){
+ nextIncludedTime =
+ getBaseCalendar().getNextIncludedTime(nextIncludedTime);
+ } else {
+ nextIncludedTime++;
+ }
+ } else {
+ //If the time is in a range excluded by this calendar, we can
+ // move to the end of the excluded time range and continue
+ // testing from there. Otherwise, if nextIncludedTime is
+ // excluded by the baseCalendar, ask it the next time it
+ // includes and begin testing from there. Failing this, add one
+ // millisecond and continue testing.
+ if (nextIncludedTime <
+ getTimeRangeStartingTimeInMillis(nextIncludedTime)) {
+ nextIncludedTime =
+ getTimeRangeStartingTimeInMillis(nextIncludedTime);
+ } else if (nextIncludedTime >
+ getTimeRangeEndingTimeInMillis(nextIncludedTime)) {
+ //(move to start of next day)
+ nextIncludedTime = getEndOfDayJavaCalendar(nextIncludedTime).getTime().getTime();
+ nextIncludedTime += 1l;
+ } else if ((getBaseCalendar() != null) &&
+ (!getBaseCalendar().isTimeIncluded(nextIncludedTime))){
+ nextIncludedTime =
+ getBaseCalendar().getNextIncludedTime(nextIncludedTime);
+ } else {
+ nextIncludedTime++;
+ }
+ }
+ }
+
+ return nextIncludedTime;
+ }
+
+ /**
+ * Returns the start time of the time range (in milliseconds) of the day
+ * specified in timeInMillis
+ *
+ * @param timeInMillis a time containing the desired date for the starting
+ * time of the time range.
+ * @return a date/time (in milliseconds) representing the start time of the
+ * time range for the specified date.
+ */
+ public long getTimeRangeStartingTimeInMillis(long timeInMillis) {
+ Calendar rangeStartingTime = createJavaCalendar(timeInMillis);
+ rangeStartingTime.set(Calendar.HOUR_OF_DAY, rangeStartingHourOfDay);
+ rangeStartingTime.set(Calendar.MINUTE, rangeStartingMinute);
+ rangeStartingTime.set(Calendar.SECOND, rangeStartingSecond);
+ rangeStartingTime.set(Calendar.MILLISECOND, rangeStartingMillis);
+ return rangeStartingTime.getTime().getTime();
+ }
+
+ /**
+ * Returns the end time of the time range (in milliseconds) of the day
+ * specified in timeInMillis
+ *
+ * @param timeInMillis a time containing the desired date for the ending
+ * time of the time range.
+ * @return a date/time (in milliseconds) representing the end time of the
+ * time range for the specified date.
+ */
+ public long getTimeRangeEndingTimeInMillis(long timeInMillis) {
+ Calendar rangeEndingTime = createJavaCalendar(timeInMillis);
+ rangeEndingTime.set(Calendar.HOUR_OF_DAY, rangeEndingHourOfDay);
+ rangeEndingTime.set(Calendar.MINUTE, rangeEndingMinute);
+ rangeEndingTime.set(Calendar.SECOND, rangeEndingSecond);
+ rangeEndingTime.set(Calendar.MILLISECOND, rangeEndingMillis);
+ return rangeEndingTime.getTime().getTime();
+ }
+
+ /**
+ * Indicates whether the time range represents an inverted time range (see
+ * class description).
+ *
+ * @return a boolean indicating whether the time range is inverted
+ */
+ public boolean getInvertTimeRange() {
+ return invertTimeRange;
+ }
+
+ /**
+ * Indicates whether the time range represents an inverted time range (see
+ * class description).
+ *
+ * @param flag the new value for the invertTimeRange
flag.
+ */
+ public void setInvertTimeRange(boolean flag) {
+ this.invertTimeRange = flag;
+ }
+
+ /**
+ * Returns a string representing the properties of the
+ * DailyCalendar
+ *
+ * @return the properteis of the DailyCalendar in a String format
+ */
+ public String toString() {
+ NumberFormat numberFormatter = NumberFormat.getNumberInstance();
+ numberFormatter.setMaximumFractionDigits(0);
+ numberFormatter.setMinimumIntegerDigits(2);
+ StringBuffer buffer = new StringBuffer();
+ if (name != null) {
+ buffer.append(name).append(": ");
+ }
+ buffer.append("base calendar: [");
+ if (getBaseCalendar() != null) {
+ buffer.append(getBaseCalendar().toString());
+ } else {
+ buffer.append("null");
+ }
+ buffer.append("], time range: '");
+ buffer.append(numberFormatter.format(rangeStartingHourOfDay));
+ buffer.append(":");
+ buffer.append(numberFormatter.format(rangeStartingMinute));
+ buffer.append(":");
+ buffer.append(numberFormatter.format(rangeStartingSecond));
+ buffer.append(":");
+ numberFormatter.setMinimumIntegerDigits(3);
+ buffer.append(numberFormatter.format(rangeStartingMillis));
+ numberFormatter.setMinimumIntegerDigits(2);
+ buffer.append(" - ");
+ buffer.append(numberFormatter.format(rangeEndingHourOfDay));
+ buffer.append(":");
+ buffer.append(numberFormatter.format(rangeEndingMinute));
+ buffer.append(":");
+ buffer.append(numberFormatter.format(rangeEndingSecond));
+ buffer.append(":");
+ numberFormatter.setMinimumIntegerDigits(3);
+ buffer.append(numberFormatter.format(rangeEndingMillis));
+ buffer.append("', inverted: " + invertTimeRange + "]");
+ return buffer.toString();
+ }
+
+ /**
+ * Helper method to split the given string by the given delimiter.
+ */
+ private String[] split(String string, String delim) {
+ ArrayList result = new ArrayList();
+
+ StringTokenizer stringTokenizer = new StringTokenizer(string, delim);
+ while (stringTokenizer.hasMoreTokens()) {
+ result.add(stringTokenizer.nextToken());
+ }
+
+ return (String[])result.toArray(new String[result.size()]);
+ }
+
+ /**
+ * Sets the time range for the DailyCalendar
to the times
+ * represented in the specified Strings.
+ *
+ * @param rangeStartingTimeString a String representing the start time of
+ * the time range
+ * @param rangeEndingTimeString a String representing the end time of the
+ * excluded time range
+ */
+ public void setTimeRange(String rangeStartingTimeString,
+ String rangeEndingTimeString) {
+ String[] rangeStartingTime;
+ int rangeStartingHourOfDay;
+ int rangeStartingMinute;
+ int rangeStartingSecond;
+ int rangeStartingMillis;
+
+ String[] rangeEndingTime;
+ int rangeEndingHourOfDay;
+ int rangeEndingMinute;
+ int rangeEndingSecond;
+ int rangeEndingMillis;
+
+ rangeStartingTime = split(rangeStartingTimeString, colon);
+
+ if ((rangeStartingTime.length < 2) || (rangeStartingTime.length > 4)) {
+ throw new IllegalArgumentException("Invalid time string '" +
+ rangeStartingTimeString + "'");
+ }
+
+ rangeStartingHourOfDay = Integer.parseInt(rangeStartingTime[0]);
+ rangeStartingMinute = Integer.parseInt(rangeStartingTime[1]);
+ if (rangeStartingTime.length > 2) {
+ rangeStartingSecond = Integer.parseInt(rangeStartingTime[2]);
+ } else {
+ rangeStartingSecond = 0;
+ }
+ if (rangeStartingTime.length == 4) {
+ rangeStartingMillis = Integer.parseInt(rangeStartingTime[3]);
+ } else {
+ rangeStartingMillis = 0;
+ }
+
+ rangeEndingTime = split(rangeEndingTimeString, colon);
+
+ if ((rangeEndingTime.length < 2) || (rangeEndingTime.length > 4)) {
+ throw new IllegalArgumentException("Invalid time string '" +
+ rangeEndingTimeString + "'");
+ }
+
+ rangeEndingHourOfDay = Integer.parseInt(rangeEndingTime[0]);
+ rangeEndingMinute = Integer.parseInt(rangeEndingTime[1]);
+ if (rangeEndingTime.length > 2) {
+ rangeEndingSecond = Integer.parseInt(rangeEndingTime[2]);
+ } else {
+ rangeEndingSecond = 0;
+ }
+ if (rangeEndingTime.length == 4) {
+ rangeEndingMillis = Integer.parseInt(rangeEndingTime[3]);
+ } else {
+ rangeEndingMillis = 0;
+ }
+
+ setTimeRange(rangeStartingHourOfDay,
+ rangeStartingMinute,
+ rangeStartingSecond,
+ rangeStartingMillis,
+ rangeEndingHourOfDay,
+ rangeEndingMinute,
+ rangeEndingSecond,
+ rangeEndingMillis);
+ }
+
+ /**
+ * Sets the time range for the DailyCalendar
to the times
+ * represented in the specified values.
+ *
+ * @param rangeStartingHourOfDay the hour of the start of the time range
+ * @param rangeStartingMinute the minute of the start of the time range
+ * @param rangeStartingSecond the second of the start of the time range
+ * @param rangeStartingMillis the millisecond of the start of the time
+ * range
+ * @param rangeEndingHourOfDay the hour of the end of the time range
+ * @param rangeEndingMinute the minute of the end of the time range
+ * @param rangeEndingSecond the second of the end of the time range
+ * @param rangeEndingMillis the millisecond of the start of the time
+ * range
+ */
+ public void setTimeRange(int rangeStartingHourOfDay,
+ int rangeStartingMinute,
+ int rangeStartingSecond,
+ int rangeStartingMillis,
+ int rangeEndingHourOfDay,
+ int rangeEndingMinute,
+ int rangeEndingSecond,
+ int rangeEndingMillis) {
+ validate(rangeStartingHourOfDay,
+ rangeStartingMinute,
+ rangeStartingSecond,
+ rangeStartingMillis);
+
+ validate(rangeEndingHourOfDay,
+ rangeEndingMinute,
+ rangeEndingSecond,
+ rangeEndingMillis);
+
+ Calendar startCal = createJavaCalendar();
+ startCal.set(Calendar.HOUR_OF_DAY, rangeStartingHourOfDay);
+ startCal.set(Calendar.MINUTE, rangeStartingMinute);
+ startCal.set(Calendar.SECOND, rangeStartingSecond);
+ startCal.set(Calendar.MILLISECOND, rangeStartingMillis);
+
+ Calendar endCal = createJavaCalendar();
+ endCal.set(Calendar.HOUR_OF_DAY, rangeEndingHourOfDay);
+ endCal.set(Calendar.MINUTE, rangeEndingMinute);
+ endCal.set(Calendar.SECOND, rangeEndingSecond);
+ endCal.set(Calendar.MILLISECOND, rangeEndingMillis);
+
+ if (!startCal.before(endCal)) {
+ throw new IllegalArgumentException(invalidTimeRange +
+ rangeStartingHourOfDay + ":" +
+ rangeStartingMinute + ":" +
+ rangeStartingSecond + ":" +
+ rangeStartingMillis + separator +
+ rangeEndingHourOfDay + ":" +
+ rangeEndingMinute + ":" +
+ rangeEndingSecond + ":" +
+ rangeEndingMillis);
+ }
+
+ this.rangeStartingHourOfDay = rangeStartingHourOfDay;
+ this.rangeStartingMinute = rangeStartingMinute;
+ this.rangeStartingSecond = rangeStartingSecond;
+ this.rangeStartingMillis = rangeStartingMillis;
+ this.rangeEndingHourOfDay = rangeEndingHourOfDay;
+ this.rangeEndingMinute = rangeEndingMinute;
+ this.rangeEndingSecond = rangeEndingSecond;
+ this.rangeEndingMillis = rangeEndingMillis;
+ }
+
+ /**
+ * Sets the time range for the DailyCalendar
to the times
+ * represented in the specified java.util.Calendar
s.
+ *
+ * @param rangeStartingCalendar a Calendar containing the start time for
+ * the DailyCalendar
+ * @param rangeEndingCalendar a Calendar containing the end time for
+ * the DailyCalendar
+ */
+ public void setTimeRange(Calendar rangeStartingCalendar,
+ Calendar rangeEndingCalendar) {
+ setTimeRange(
+ rangeStartingCalendar.get(Calendar.HOUR_OF_DAY),
+ rangeStartingCalendar.get(Calendar.MINUTE),
+ rangeStartingCalendar.get(Calendar.SECOND),
+ rangeStartingCalendar.get(Calendar.MILLISECOND),
+ rangeEndingCalendar.get(Calendar.HOUR_OF_DAY),
+ rangeEndingCalendar.get(Calendar.MINUTE),
+ rangeEndingCalendar.get(Calendar.SECOND),
+ rangeEndingCalendar.get(Calendar.MILLISECOND));
+ }
+
+ /**
+ * Sets the time range for the DailyCalendar
to the times
+ * represented in the specified values.
+ *
+ * @param rangeStartingTime the starting time (in milliseconds) for the
+ * time range
+ * @param rangeEndingTime the ending time (in milliseconds) for the time
+ * range
+ */
+ public void setTimeRange(long rangeStartingTime,
+ long rangeEndingTime) {
+ setTimeRange(
+ createJavaCalendar(rangeStartingTime),
+ createJavaCalendar(rangeEndingTime));
+ }
+
+ /**
+ * Checks the specified values for validity as a set of time values.
+ *
+ * @param hourOfDay the hour of the time to check (in military (24-hour)
+ * time)
+ * @param minute the minute of the time to check
+ * @param second the second of the time to check
+ * @param millis the millisecond of the time to check
+ */
+ private void validate(int hourOfDay, int minute, int second, int millis) {
+ if (hourOfDay < 0 || hourOfDay > 23) {
+ throw new IllegalArgumentException(invalidHourOfDay + hourOfDay);
+ }
+ if (minute < 0 || minute > 59) {
+ throw new IllegalArgumentException(invalidMinute + minute);
+ }
+ if (second < 0 || second > 59) {
+ throw new IllegalArgumentException(invalidSecond + second);
+ }
+ if (millis < 0 || millis > 999) {
+ throw new IllegalArgumentException(invalidMillis + millis);
+ }
+ }
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/HolidayCalendar.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/HolidayCalendar.java
new file mode 100644
index 000000000..9f511842a
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/HolidayCalendar.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl.calendar;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Date;
+import java.util.SortedSet;
+import java.util.TimeZone;
+import java.util.TreeSet;
+
+import com.fr.third.org.quartz.Calendar;
+
+/**
+ * + * This implementation of the Calendar stores a list of holidays (full days + * that are excluded from scheduling). + *
+ * + *+ * The implementation DOES take the year into consideration, so if you want to + * exclude July 4th for the next 10 years, you need to add 10 entries to the + * exclude list. + *
+ * + * @author Sharada Jambula + * @author Juergen Donnerstag + */ +public class HolidayCalendar extends BaseCalendar implements Calendar, + Serializable { + static final long serialVersionUID = -7590908752291814693L; + + // A sorted set to store the holidays + private TreeSet dates = new TreeSet(); + + public HolidayCalendar() { + } + + public HolidayCalendar(Calendar baseCalendar) { + super(baseCalendar); + } + + public HolidayCalendar(TimeZone timeZone) { + super(timeZone); + } + + public HolidayCalendar(Calendar baseCalendar, TimeZone timeZone) { + super(baseCalendar, timeZone); + } + + /** + *+ * Determine whether the given time (in milliseconds) is 'included' by the + * Calendar. + *
+ * + *+ * Note that this Calendar is only has full-day precision. + *
+ */ + public boolean isTimeIncluded(long timeStamp) { + if (super.isTimeIncluded(timeStamp) == false) { + return false; + } + + Date lookFor = getStartOfDayJavaCalendar(timeStamp).getTime(); + + return !(dates.contains(lookFor)); + } + + /** + *+ * Determine the next time (in milliseconds) that is 'included' by the + * Calendar after the given time. + *
+ * + *+ * Note that this Calendar is only has full-day precision. + *
+ */ + public long getNextIncludedTime(long timeStamp) { + + // Call base calendar implementation first + long baseTime = super.getNextIncludedTime(timeStamp); + if ((baseTime > 0) && (baseTime > timeStamp)) { + timeStamp = baseTime; + } + + // Get timestamp for 00:00:00 + java.util.Calendar day = getStartOfDayJavaCalendar(timeStamp); + while (isTimeIncluded(day.getTime().getTime()) == false) { + day.add(java.util.Calendar.DATE, 1); + } + + return day.getTime().getTime(); + } + + /** + *+ * Add the given Date to the list of excluded days. Only the month, day and + * year of the returned dates are significant. + *
+ */ + public void addExcludedDate(Date excludedDate) { + Date date = getStartOfDayJavaCalendar(excludedDate.getTime()).getTime(); + /* + * System.err.println( "HolidayCalendar.add(): date=" + + * excludedDate.toLocaleString()); + */ + this.dates.add(date); + } + + public void removeExcludedDate(Date dateToRemove) { + Date date = getStartOfDayJavaCalendar(dateToRemove.getTime()).getTime(); + dates.remove(date); + } + + /** + *
+ * Returns a SortedSet
of Dates representing the excluded
+ * days. Only the month, day and year of the returned dates are
+ * significant.
+ *
+ * This implementation of the Calendar excludes a set of days of the month. You + * may use it to exclude every first day of each month for example. But you may define + * any day of a month. + *
+ * + * @see com.fr.third.org.quartz.Calendar + * @see com.fr.third.org.quartz.impl.calendar.BaseCalendar + * + * @author Juergen Donnerstag + */ +public class MonthlyCalendar extends BaseCalendar implements Calendar, + Serializable { + + static final long serialVersionUID = 419164961091807944L; + + private static final int MAX_DAYS_IN_MONTH = 31; + + // An array to store a months days which are to be excluded. + // java.util.Calendar.get( ) as index. + private boolean[] excludeDays = new boolean[MAX_DAYS_IN_MONTH]; + + // Will be set to true, if all week days are excluded + private boolean excludeAll = false; + + public MonthlyCalendar() { + this(null, null); + } + + public MonthlyCalendar(Calendar baseCalendar) { + this(baseCalendar, null); + } + + public MonthlyCalendar(TimeZone timeZone) { + this(null, timeZone); + } + + public MonthlyCalendar(Calendar baseCalendar, TimeZone timeZone) { + super(baseCalendar, timeZone); + + // all days are included by default + excludeAll = areAllDaysExcluded(); + } + + /** + *+ * Get the array which defines the exclude-value of each day of month. + * Only the first 31 elements of the array are relevant, with the 0 index + * element representing the first day of the month. + *
+ */ + public boolean[] getDaysExcluded() { + return excludeDays; + } + + /** + *+ * Return true, if day is defined to be excluded. + *
+ * + * @param day The day of the month (from 1 to 31) to check. + */ + public boolean isDayExcluded(int day) { + if ((day < 1) || (day > MAX_DAYS_IN_MONTH)) { + throw new IllegalArgumentException( + "The day parameter must be in the range of 1 to " + MAX_DAYS_IN_MONTH); + } + + return excludeDays[day - 1]; + } + + /** + *+ * Redefine the array of days excluded. The array must non-null and of size + * greater or equal to 31. The 0 index element represents the first day of + * the month. + *
+ */ + public void setDaysExcluded(boolean[] days) { + if (days == null) { + throw new IllegalArgumentException("The days parameter cannot be null."); + } + + if (days.length < MAX_DAYS_IN_MONTH) { + throw new IllegalArgumentException( + "The days parameter must have a length of at least " + MAX_DAYS_IN_MONTH + " elements."); + } + + excludeDays = days; + excludeAll = areAllDaysExcluded(); + } + + /** + *+ * Redefine a certain day of the month to be excluded (true) or included + * (false). + *
+ * + * @param day The day of the month (from 1 to 31) to set. + */ + public void setDayExcluded(int day, boolean exclude) { + if ((day < 1) || (day > MAX_DAYS_IN_MONTH)) { + throw new IllegalArgumentException( + "The day parameter must be in the range of 1 to " + MAX_DAYS_IN_MONTH); + } + + excludeDays[day - 1] = exclude; + excludeAll = areAllDaysExcluded(); + } + + /** + *+ * Check if all days are excluded. That is no day is included. + *
+ */ + public boolean areAllDaysExcluded() { + for (int i = 1; i <= MAX_DAYS_IN_MONTH; i++) { + if (isDayExcluded(i) == false) { + return false; + } + } + + return true; + } + + /** + *+ * Determine whether the given time (in milliseconds) is 'included' by the + * Calendar. + *
+ * + *+ * Note that this Calendar is only has full-day precision. + *
+ */ + public boolean isTimeIncluded(long timeStamp) { + if (excludeAll == true) { + return false; + } + + // Test the base calendar first. Only if the base calendar not already + // excludes the time/date, continue evaluating this calendar instance. + if (super.isTimeIncluded(timeStamp) == false) { return false; } + + java.util.Calendar cl = createJavaCalendar(timeStamp); + int day = cl.get(java.util.Calendar.DAY_OF_MONTH); + + return !(isDayExcluded(day)); + } + + /** + *+ * Determine the next time (in milliseconds) that is 'included' by the + * Calendar after the given time. Return the original value if timeStamp is + * included. Return 0 if all days are excluded. + *
+ * + *+ * Note that this Calendar is only has full-day precision. + *
+ */ + public long getNextIncludedTime(long timeStamp) { + if (excludeAll == true) { + return 0; + } + + // Call base calendar implementation first + long baseTime = super.getNextIncludedTime(timeStamp); + if ((baseTime > 0) && (baseTime > timeStamp)) { + timeStamp = baseTime; + } + + // Get timestamp for 00:00:00 + java.util.Calendar cl = getStartOfDayJavaCalendar(timeStamp); + int day = cl.get(java.util.Calendar.DAY_OF_MONTH); + + if (!isDayExcluded(day)) { + return timeStamp; // return the original value + } + + while (isDayExcluded(day) == true) { + cl.add(java.util.Calendar.DATE, 1); + day = cl.get(java.util.Calendar.DAY_OF_MONTH); + } + + return cl.getTime().getTime(); + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/WeeklyCalendar.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/WeeklyCalendar.java new file mode 100644 index 000000000..f24872d51 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/calendar/WeeklyCalendar.java @@ -0,0 +1,200 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + * and Juergen Donnerstag (c) 2002, EDS 2002 + */ + +package com.fr.third.org.quartz.impl.calendar; + +import java.io.Serializable; +import java.util.TimeZone; + +import com.fr.third.org.quartz.Calendar; + +/** + *+ * This implementation of the Calendar excludes a set of days of the week. You + * may use it to exclude weekends for example. But you may define any day of + * the week. By default it excludes SATURDAY and SUNDAY. + *
+ * + * @see com.fr.third.org.quartz.Calendar + * @see com.fr.third.org.quartz.impl.calendar.BaseCalendar + * + * @author Juergen Donnerstag + */ +public class WeeklyCalendar extends BaseCalendar implements Calendar, + Serializable { + static final long serialVersionUID = -6809298821229007586L; + + // An array to store the week days which are to be excluded. + // java.util.Calendar.MONDAY etc. are used as index. + private boolean[] excludeDays = new boolean[8]; + + // Will be set to true, if all week days are excluded + private boolean excludeAll = false; + + public WeeklyCalendar() { + this(null, null); + } + + public WeeklyCalendar(Calendar baseCalendar) { + this(baseCalendar, null); + } + + public WeeklyCalendar(TimeZone timeZone) { + super(null, timeZone); + } + + public WeeklyCalendar(Calendar baseCalendar, TimeZone timeZone) { + super(baseCalendar, timeZone); + + excludeDays[java.util.Calendar.SUNDAY] = true; + excludeDays[java.util.Calendar.SATURDAY] = true; + excludeAll = areAllDaysExcluded(); + } + + /** + *+ * Get the array with the week days + *
+ */ + public boolean[] getDaysExcluded() { + return excludeDays; + } + + /** + *+ * Return true, if wday (see Calendar.get()) is defined to be exluded. E. g. + * saturday and sunday. + *
+ */ + public boolean isDayExcluded(int wday) { + return excludeDays[wday]; + } + + /** + *+ * Redefine the array of days excluded. The array must of size greater or + * equal 8. java.util.Calendar's constants like MONDAY should be used as + * index. A value of true is regarded as: exclude it. + *
+ */ + public void setDaysExcluded(boolean[] weekDays) { + if (weekDays == null) { + return; + } + + excludeDays = weekDays; + excludeAll = areAllDaysExcluded(); + } + + /** + *+ * Redefine a certain day of the week to be excluded (true) or included + * (false). Use java.util.Calendar's constants like MONDAY to determine the + * wday. + *
+ */ + public void setDayExcluded(int wday, boolean exclude) { + excludeDays[wday] = exclude; + excludeAll = areAllDaysExcluded(); + } + + /** + *+ * Check if all week days are excluded. That is no day is included. + *
+ * + * @return boolean + */ + public boolean areAllDaysExcluded() { + return + isDayExcluded(java.util.Calendar.SUNDAY) && + isDayExcluded(java.util.Calendar.MONDAY) && + isDayExcluded(java.util.Calendar.TUESDAY) && + isDayExcluded(java.util.Calendar.WEDNESDAY) && + isDayExcluded(java.util.Calendar.THURSDAY) && + isDayExcluded(java.util.Calendar.FRIDAY) && + isDayExcluded(java.util.Calendar.SATURDAY); + } + + /** + *+ * Determine whether the given time (in milliseconds) is 'included' by the + * Calendar. + *
+ * + *+ * Note that this Calendar is only has full-day precision. + *
+ */ + public boolean isTimeIncluded(long timeStamp) { + if (excludeAll == true) { + return false; + } + + // Test the base calendar first. Only if the base calendar not already + // excludes the time/date, continue evaluating this calendar instance. + if (super.isTimeIncluded(timeStamp) == false) { return false; } + + java.util.Calendar cl = createJavaCalendar(timeStamp); + int wday = cl.get(java.util.Calendar.DAY_OF_WEEK); + + return !(isDayExcluded(wday)); + } + + /** + *+ * Determine the next time (in milliseconds) that is 'included' by the + * Calendar after the given time. Return the original value if timeStamp is + * included. Return 0 if all days are excluded. + *
+ * + *+ * Note that this Calendar is only has full-day precision. + *
+ */ + public long getNextIncludedTime(long timeStamp) { + if (excludeAll == true) { + return 0; + } + + // Call base calendar implementation first + long baseTime = super.getNextIncludedTime(timeStamp); + if ((baseTime > 0) && (baseTime > timeStamp)) { + timeStamp = baseTime; + } + + // Get timestamp for 00:00:00 + java.util.Calendar cl = getStartOfDayJavaCalendar(timeStamp); + int wday = cl.get(java.util.Calendar.DAY_OF_WEEK); + + if (!isDayExcluded(wday)) { + return timeStamp; // return the original value + } + + while (isDayExcluded(wday) == true) { + cl.add(java.util.Calendar.DATE, 1); + wday = cl.get(java.util.Calendar.DAY_OF_WEEK); + } + + return cl.getTime().getTime(); + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/AttributeRestoringConnectionInvocationHandler.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/AttributeRestoringConnectionInvocationHandler.java new file mode 100644 index 000000000..7d3906ab1 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/AttributeRestoringConnectionInvocationHandler.java @@ -0,0 +1,159 @@ +/* + * Copyright 2004-2006 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.fr.third.org.quartz.impl.jdbcjobstore; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + *
+ * Protects a {@link java.sql.Connection}
's attributes from being permanently modfied.
+ *
+ * Wraps a provided {@link java.sql.Connection}
such that its auto
+ * commit and transaction isolation attributes can be overwritten, but
+ * will automatically restored to their original values when the connection
+ * is actually closed (and potentially returned to a pool for reuse).
+ *
java.sql.Connection
interface.
+ *
+ * @return The underlying connection to which all operations
+ * ultimately defer.
+ */
+ public Connection getWrappedConnection() {
+ return conn;
+ }
+
+ /**
+ * Attempts to restore the auto commit and transaction isolation connection
+ * attributes of the wrapped connection to their original values (if they
+ * were overwritten).
+ */
+ public void restoreOriginalAtributes() {
+ try {
+ if (overwroteOriginalAutoCommitValue) {
+ conn.setAutoCommit(originalAutoCommitValue);
+ }
+ } catch (Throwable t) {
+ getLog().warn("Failed restore connection's original auto commit setting.", t);
+ }
+
+ try {
+ if (overwroteOriginalTxIsolationValue) {
+ conn.setTransactionIsolation(originalTxIsolationValue);
+ }
+ } catch (Throwable t) {
+ getLog().warn("Failed restore connection's original transaction isolation setting.", t);
+ }
+ }
+
+ /**
+ * Attempts to restore the auto commit and transaction isolation connection
+ * attributes of the wrapped connection to their original values (if they
+ * were overwritten), before finally actually closing the wrapped connection.
+ */
+ public void close() throws SQLException {
+ restoreOriginalAtributes();
+
+ conn.close();
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/CloudscapeDelegate.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/CloudscapeDelegate.java
new file mode 100644
index 000000000..9a938c62b
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/CloudscapeDelegate.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * + * This is a driver delegate for the Cloudscape database, not surprisingly, + * it is known to work with Derby as well. Though later versions of Derby + * may simply use StdJDBCDelegate. + *
+ * + * @author James House + * @author Sridhar Jawaharlal, Srinivas Venkatarangaiah + * @deprecated Use the StdJDBCDelegate for latest versions of Derby + */ +public class CloudscapeDelegate extends StdJDBCDelegate { + /** + *+ * Create new CloudscapeDelegate instance. + *
+ * + * @param log + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + */ + public CloudscapeDelegate(Log log, String tablePrefix, String instanceId) { + super(log, tablePrefix, instanceId); + } + + /** + *+ * Create new CloudscapeDelegate instance. + *
+ * + * @param log + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + * @param useProperties + * useProperties flag + */ + public CloudscapeDelegate(Log log, String tablePrefix, String instanceId, + Boolean useProperties) { + super(log, tablePrefix, instanceId, useProperties); + } + + //--------------------------------------------------------------------------- + // protected methods that can be overridden by subclasses + //--------------------------------------------------------------------------- + + /** + *
+ * This method should be overridden by any delegate subclasses that need
+ * special handling for BLOBs. The default implementation uses standard
+ * JDBC java.sql.Blob
operations.
+ *
+ * This interface can be implemented by any {@link
+ * com.fr.third.org.quartz.impl.jdbcjobstore.DriverDelegate}
+ * class that needs to use the constants contained herein.
+ *
select count(name)
+ * had to be replaced with select count(*)
.
+ *
+ * @author Martin Renner
+ * @author James House
+ */
+public class DB2v6Delegate extends StdJDBCDelegate {
+ public static final String SELECT_NUM_JOBS = "SELECT COUNT(*) FROM "
+ + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS;
+
+ public static final String SELECT_NUM_TRIGGERS_FOR_JOB = "SELECT COUNT(*) FROM "
+ + TABLE_PREFIX_SUBST
+ + TABLE_TRIGGERS
+ + " WHERE "
+ + COL_JOB_NAME
+ + " = ? AND " + COL_JOB_GROUP + " = ?";
+
+ public static final String SELECT_NUM_TRIGGERS = "SELECT COUNT(*) FROM "
+ + TABLE_PREFIX_SUBST + TABLE_TRIGGERS;
+
+ public static final String SELECT_NUM_CALENDARS = "SELECT COUNT(*) FROM "
+ + TABLE_PREFIX_SUBST + TABLE_CALENDARS;
+
+ public DB2v6Delegate(Log logger, String tablePrefix, String instanceId) {
+ super(logger, tablePrefix, instanceId);
+ }
+
+ public DB2v6Delegate(Log logger, String tablePrefix, String instanceId,
+ Boolean useProperties) {
+ super(logger, tablePrefix, instanceId, useProperties);
+ }
+
+ public int selectNumJobs(Connection conn) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ int count = 0;
+ ps = conn.prepareStatement(rtp(SELECT_NUM_JOBS));
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ count = rs.getInt(1);
+ }
+
+ return count;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ public int selectNumTriggersForJob(Connection conn, String jobName,
+ String groupName) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_NUM_TRIGGERS_FOR_JOB));
+ ps.setString(1, jobName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ return rs.getInt(1);
+ } else {
+ return 0;
+ }
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ public int selectNumTriggers(Connection conn) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ int count = 0;
+ ps = conn.prepareStatement(rtp(SELECT_NUM_TRIGGERS));
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ count = rs.getInt(1);
+ }
+
+ return count;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ public int selectNumCalendars(Connection conn) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ int count = 0;
+ ps = conn.prepareStatement(rtp(SELECT_NUM_CALENDARS));
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ count = rs.getInt(1);
+ }
+
+ return count;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+}
+
+// EOF
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DB2v7Delegate.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DB2v7Delegate.java
new file mode 100644
index 000000000..6585ffbef
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DB2v7Delegate.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import java.io.ByteArrayOutputStream;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Quartz JDBC delegate for DB2 v7 databases.
+ *
+ * This differs from the StdJDBCDelegate
in that it stores
+ * boolean
values in an varchar(1)
column, and saves
+ * serialized data in a byte array using
+ * {@link PreparedStatement#setObject(int, java.lang.Object, int)}
+ * rather than {@link PreparedStatement#setBytes(int, byte[])}
.
+ *
ByteArrayOutputStream
. Will set parameter value to null if the
+ * ByteArrayOutputStream
is null.
+ * Wraps {@link PreparedStatement#setObject(int, java.lang.Object, int)}
rather than
+ * {@link PreparedStatement#setBytes(int, byte[])}
as required by the
+ * DB2 v7 database.
+ */
+ protected void setBytes(PreparedStatement ps, int index, ByteArrayOutputStream baos) throws SQLException {
+ ps.setObject(index, ((baos == null) ? null : baos.toByteArray()), java.sql.Types.BLOB);
+ }
+
+ /**
+ * Sets the designated parameter to the given Java boolean
value.
+ * This translates the boolean to 1/0 for true/false.
+ */
+ protected void setBoolean(PreparedStatement ps, int index, boolean val) throws SQLException {
+ ps.setString(index, ((val) ? "1" : "0"));
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DB2v8Delegate.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DB2v8Delegate.java
new file mode 100644
index 000000000..f8b93966b
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DB2v8Delegate.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Quartz JDBC delegate for DB2 v8 databases.
+ *
+ * This differs from the StdJDBCDelegate
in that it stores
+ * boolean
values in an integer
column.
+ *
boolean
value.
+ * This translates the boolean to 1/0 for true/false.
+ */
+ protected void setBoolean(PreparedStatement ps, int index, boolean val) throws SQLException {
+ ps.setInt(index, ((val) ? 1 : 0));
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DBSemaphore.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DBSemaphore.java
new file mode 100644
index 000000000..092cce504
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DBSemaphore.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2004-2006 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import java.sql.Connection;
+import java.util.HashSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Base class for database based lock handlers for providing thread/resource locking
+ * in order to protect resources from being altered by multiple threads at the
+ * same time.
+ */
+public abstract class DBSemaphore implements Semaphore, Constants,
+ StdJDBCConstants, TablePrefixAware {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ ThreadLocal lockOwners = new ThreadLocal();
+
+ private String sql;
+
+ private String tablePrefix;
+
+ private String expandedSQL;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public DBSemaphore(String tablePrefix, String sql, String defaultSQL) {
+ this.sql = defaultSQL;
+ this.tablePrefix = tablePrefix;
+ setSQL(sql);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ protected Log getLog() {
+ return log;
+ }
+
+ private HashSet getThreadLocks() {
+ HashSet threadLocks = (HashSet) lockOwners.get();
+ if (threadLocks == null) {
+ threadLocks = new HashSet();
+ lockOwners.set(threadLocks);
+ }
+ return threadLocks;
+ }
+
+ /**
+ * Execute the SQL that will lock the proper database row.
+ */
+ protected abstract void executeSQL(
+ Connection conn, String lockName, String expandedSQL) throws LockException;
+
+ /**
+ * Grants a lock on the identified resource to the calling thread (blocking
+ * until it is available).
+ *
+ * @return true if the lock was obtained.
+ */
+ public boolean obtainLock(Connection conn, String lockName)
+ throws LockException {
+
+ lockName = lockName.intern();
+
+ Log log = getLog();
+
+ if(log.isDebugEnabled()) {
+ log.debug(
+ "Lock '" + lockName + "' is desired by: "
+ + Thread.currentThread().getName());
+ }
+ if (!isLockOwner(conn, lockName)) {
+
+ executeSQL(conn, lockName, expandedSQL);
+
+ if(log.isDebugEnabled()) {
+ log.debug(
+ "Lock '" + lockName + "' given to: "
+ + Thread.currentThread().getName());
+ }
+ getThreadLocks().add(lockName);
+ //getThreadLocksObtainer().put(lockName, new
+ // Exception("Obtainer..."));
+ } else if(log.isDebugEnabled()) {
+ log.debug(
+ "Lock '" + lockName + "' Is already owned by: "
+ + Thread.currentThread().getName());
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Release the lock on the identified resource if it is held by the calling
+ * thread.
+ */
+ public void releaseLock(Connection conn, String lockName) {
+
+ lockName = lockName.intern();
+
+ if (isLockOwner(conn, lockName)) {
+ if(getLog().isDebugEnabled()) {
+ getLog().debug(
+ "Lock '" + lockName + "' returned by: "
+ + Thread.currentThread().getName());
+ }
+ getThreadLocks().remove(lockName);
+ //getThreadLocksObtainer().remove(lockName);
+ } else if (getLog().isDebugEnabled()) {
+ getLog().warn(
+ "Lock '" + lockName + "' attempt to return by: "
+ + Thread.currentThread().getName()
+ + " -- but not owner!",
+ new Exception("stack-trace of wrongful returner"));
+ }
+ }
+
+ /**
+ * Determine whether the calling thread owns a lock on the identified
+ * resource.
+ */
+ public boolean isLockOwner(Connection conn, String lockName) {
+ lockName = lockName.intern();
+
+ return getThreadLocks().contains(lockName);
+ }
+
+ /**
+ * This Semaphore implementation does use the database.
+ */
+ public boolean requiresConnection() {
+ return true;
+ }
+
+ protected String getSQL() {
+ return sql;
+ }
+
+ protected void setSQL(String sql) {
+ if ((sql != null) && (sql.trim().length() != 0)) {
+ this.sql = sql;
+ }
+
+ setExpandedSQL();
+ }
+
+ private void setExpandedSQL() {
+ if (getTablePrefix() != null) {
+ expandedSQL = Util.rtp(this.sql, getTablePrefix());
+ }
+ }
+
+ protected String getTablePrefix() {
+ return tablePrefix;
+ }
+
+ public void setTablePrefix(String tablePrefix) {
+ this.tablePrefix = tablePrefix;
+
+ setExpandedSQL();
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DriverDelegate.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DriverDelegate.java
new file mode 100644
index 000000000..6fd861976
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/DriverDelegate.java
@@ -0,0 +1,1435 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Set;
+
+import com.fr.third.org.quartz.Calendar;
+import com.fr.third.org.quartz.CronTrigger;
+import com.fr.third.org.quartz.JobDataMap;
+import com.fr.third.org.quartz.JobDetail;
+import com.fr.third.org.quartz.SimpleTrigger;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.spi.ClassLoadHelper;
+import com.fr.third.org.quartz.utils.Key;
+import com.fr.third.org.quartz.utils.TriggerStatus;
+
+/**
+ * + * This is the base interface for all driver delegate classes. + *
+ * + *
+ * This interface is very similar to the {@link
+ * com.fr.third.org.quartz.spi.JobStore}
+ * interface except each method has an additional {@link java.sql.Connection}
+ * parameter.
+ *
+ * Unless a database driver has some extremely-DB-specific
+ * requirements, any DriverDelegate implementation classes should extend the
+ * {@link com.fr.third.org.quartz.impl.jdbcjobstore.StdJDBCDelegate}
class.
+ *
+ * Update all triggers having one of the two given states, to the given new + * state. + *
+ * + * @param conn + * the DB Connection + * @param newState + * the new state for the triggers + * @param oldState1 + * the first old state to update + * @param oldState2 + * the second old state to update + * @return number of rows updated + */ + int updateTriggerStatesFromOtherStates(Connection conn, + String newState, String oldState1, String oldState2) + throws SQLException; + + /** + *+ * Get the names of all of the triggers that have misfired - according to + * the given timestamp. + *
+ * + * @param conn + * the DB Connection + * @return an array of{@link
+ * com.fr.third.org.quartz.utils.Key}
objects
+ */
+ Key[] selectMisfiredTriggers(Connection conn, long ts)
+ throws SQLException;
+
+ /**
+ * + * Get the names of all of the triggers in the given state that have + * misfired - according to the given timestamp. + *
+ * + * @param conn + * the DB Connection + * @return an array of{@link
+ * com.fr.third.org.quartz.utils.Key}
objects
+ */
+ Key[] selectMisfiredTriggersInState(Connection conn, String state,
+ long ts) throws SQLException;
+
+ /**
+ * + * Get the names of all of the triggers in the given states that have + * misfired - according to the given timestamp. No more than count will + * be returned. + *
+ * + * @param conn the DB Connection + * @param count the most misfired triggers to return, negative for all + * @param resultList Output parameter. A List of + *{@link com.fr.third.org.quartz.utils.Key}
objects. Must not be null.
+ *
+ * @return Whether there are more misfired triggers left to find beyond
+ * the given count.
+ */
+ boolean selectMisfiredTriggersInStates(Connection conn, String state1, String state2,
+ long ts, int count, List resultList) throws SQLException;
+
+ /**
+ * + * Get the number of triggers in the given states that have + * misfired - according to the given timestamp. + *
+ * + * @param conn the DB Connection + */ + int countMisfiredTriggersInStates( + Connection conn, String state1, String state2, long ts) throws SQLException; + + /** + *+ * Get the names of all of the triggers in the given group and state that + * have misfired - according to the given timestamp. + *
+ * + * @param conn + * the DB Connection + * @return an array of{@link
+ * com.fr.third.org.quartz.utils.Key}
objects
+ */
+ Key[] selectMisfiredTriggersInGroupInState(Connection conn,
+ String groupName, String state, long ts) throws SQLException;
+
+
+ /**
+ *
+ * Select all of the triggers for jobs that are requesting recovery. The
+ * returned trigger objects will have unique "recoverXXX" trigger names and
+ * will be in the {@link
+ * com.fr.third.org.quartz.Scheduler}.DEFAULT_RECOVERY_GROUP
+ * trigger group.
+ *
+ * In order to preserve the ordering of the triggers, the fire time will be
+ * set from the COL_FIRED_TIME
column in the TABLE_FIRED_TRIGGERS
+ * table. The caller is responsible for calling computeFirstFireTime
+ * on each returned trigger. It is also up to the caller to insert the
+ * returned triggers to ensure that they are fired.
+ *
{@link com.fr.third.org.quartz.Trigger}
objects
+ */
+ Trigger[] selectTriggersForRecoveringJobs(Connection conn)
+ throws SQLException, IOException, ClassNotFoundException;
+
+ /**
+ * + * Delete all fired triggers. + *
+ * + * @param conn + * the DB Connection + * @return the number of rows deleted + */ + int deleteFiredTriggers(Connection conn) throws SQLException; + + /** + *+ * Delete all fired triggers of the given instance. + *
+ * + * @param conn + * the DB Connection + * @return the number of rows deleted + */ + int deleteFiredTriggers(Connection conn, String instanceId) + throws SQLException; + + /** + *+ * Delete all volatile fired triggers. + *
+ * + * @param conn + * the DB Connection + * @return the number of rows deleted + */ + int deleteVolatileFiredTriggers(Connection conn) throws SQLException; + + /** + *+ * Get the names of all of the triggers that are volatile. + *
+ * + * @param conn + * the DB Connection + * @return an array of{@link
+ * com.fr.third.org.quartz.utils.Key}
objects
+ */
+ Key[] selectVolatileTriggers(Connection conn) throws SQLException;
+
+ /**
+ * + * Get the names of all of the jobs that are volatile. + *
+ * + * @param conn + * the DB Connection + * @return an array of{@link
+ * com.fr.third.org.quartz.utils.Key}
objects
+ */
+ Key[] selectVolatileJobs(Connection conn) throws SQLException;
+
+ //---------------------------------------------------------------------------
+ // jobs
+ //---------------------------------------------------------------------------
+
+ /**
+ * + * Insert the job detail record. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to insert + * @return number of rows inserted + * @throws IOException + * if there were problems serializing the JobDataMap + */ + int insertJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException; + + /** + *+ * Update the job detail record. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to update + * @return number of rows updated + * @throws IOException + * if there were problems serializing the JobDataMap + */ + int updateJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException; + + /** + *+ * Get the names of all of the triggers associated with the given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the job name + * @param groupName + * the job group + * @return an array of{@link
+ * com.fr.third.org.quartz.utils.Key}
objects
+ */
+ Key[] selectTriggerNamesForJob(Connection conn, String jobName,
+ String groupName) throws SQLException;
+
+ /**
+ * + * Delete all job listeners for the given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return the number of rows deleted + */ + int deleteJobListeners(Connection conn, String jobName, + String groupName) throws SQLException; + + /** + *+ * Delete the job detail record for the given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return the number of rows deleted + */ + int deleteJobDetail(Connection conn, String jobName, String groupName) + throws SQLException; + + /** + *+ * Check whether or not the given job is stateful. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return true if the job exists and is stateful, false otherwise + */ + boolean isJobStateful(Connection conn, String jobName, + String groupName) throws SQLException; + + /** + *+ * Check whether or not the given job exists. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return true if the job exists, false otherwise + */ + boolean jobExists(Connection conn, String jobName, String groupName) + throws SQLException; + + /** + *+ * Update the job data map for the given job. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to update + * @return the number of rows updated + * @throws IOException + * if there were problems serializing the JobDataMap + */ + int updateJobData(Connection conn, JobDetail job) + throws IOException, SQLException; + + /** + *+ * Associate a listener with a job. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to associate with the listener + * @param listener + * the listener to insert + * @return the number of rows inserted + */ + int insertJobListener(Connection conn, JobDetail job, String listener) + throws SQLException; + + /** + *+ * Get all of the listeners for a given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the job name whose listeners are wanted + * @param groupName + * the group containing the job + * @return array ofString
listener names
+ */
+ String[] selectJobListeners(Connection conn, String jobName,
+ String groupName) throws SQLException;
+
+ /**
+ * + * Select the JobDetail object for a given job name / group name. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the job name whose listeners are wanted + * @param groupName + * the group containing the job + * @return the populated JobDetail object + * @throws ClassNotFoundException + * if a class found during deserialization cannot be found or if + * the job class could not be found + * @throws IOException + * if deserialization causes an error + */ + JobDetail selectJobDetail(Connection conn, String jobName, + String groupName, ClassLoadHelper loadHelper) + throws ClassNotFoundException, IOException, SQLException; + + /** + *+ * Select the total number of jobs stored. + *
+ * + * @param conn + * the DB Connection + * @return the total number of jobs stored + */ + int selectNumJobs(Connection conn) throws SQLException; + + /** + *+ * Select all of the job group names that are stored. + *
+ * + * @param conn + * the DB Connection + * @return an array ofString
group names
+ */
+ String[] selectJobGroups(Connection conn) throws SQLException;
+
+ /**
+ * + * Select all of the jobs contained in a given group. + *
+ * + * @param conn + * the DB Connection + * @param groupName + * the group containing the jobs + * @return an array ofString
job names
+ */
+ String[] selectJobsInGroup(Connection conn, String groupName)
+ throws SQLException;
+
+ //---------------------------------------------------------------------------
+ // triggers
+ //---------------------------------------------------------------------------
+
+ /**
+ * + * Insert the base trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @param state + * the state that the trigger should be stored in + * @return the number of rows inserted + */ + int insertTrigger(Connection conn, Trigger trigger, String state, + JobDetail jobDetail) throws SQLException, IOException; + + /** + *+ * Insert the simple trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows inserted + */ + int insertSimpleTrigger(Connection conn, SimpleTrigger trigger) + throws SQLException; + + /** + *+ * Insert the blob trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows inserted + */ + int insertBlobTrigger(Connection conn, Trigger trigger) + throws SQLException, IOException; + + /** + *+ * Insert the cron trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows inserted + */ + int insertCronTrigger(Connection conn, CronTrigger trigger) + throws SQLException; + + /** + *+ * Update the base trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @param state + * the state that the trigger should be stored in + * @return the number of rows updated + */ + int updateTrigger(Connection conn, Trigger trigger, String state, + JobDetail jobDetail) throws SQLException, IOException; + + /** + *+ * Update the simple trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows updated + */ + int updateSimpleTrigger(Connection conn, SimpleTrigger trigger) + throws SQLException; + + /** + *+ * Update the cron trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows updated + */ + int updateCronTrigger(Connection conn, CronTrigger trigger) + throws SQLException; + + /** + *+ * Update the blob trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows updated + */ + int updateBlobTrigger(Connection conn, Trigger trigger) + throws SQLException, IOException; + + /** + *+ * Check whether or not a trigger exists. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the number of rows updated + */ + boolean triggerExists(Connection conn, String triggerName, + String groupName) throws SQLException; + + /** + *+ * Update the state for a given trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @param state + * the new state for the trigger + * @return the number of rows updated + */ + int updateTriggerState(Connection conn, String triggerName, + String groupName, String state) throws SQLException; + + /** + *+ * Update the given trigger to the given new state, if it is in the given + * old state. + *
+ * + * @param conn + * the DB connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @param newState + * the new state for the trigger + * @param oldState + * the old state the trigger must be in + * @return int the number of rows updated + * @throws SQLException + */ + int updateTriggerStateFromOtherState(Connection conn, + String triggerName, String groupName, String newState, + String oldState) throws SQLException; + + /** + *+ * Update the given trigger to the given new state, if it is one of the + * given old states. + *
+ * + * @param conn + * the DB connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @param newState + * the new state for the trigger + * @param oldState1 + * one of the old state the trigger must be in + * @param oldState2 + * one of the old state the trigger must be in + * @param oldState3 + * one of the old state the trigger must be in + * @return int the number of rows updated + * @throws SQLException + */ + int updateTriggerStateFromOtherStates(Connection conn, + String triggerName, String groupName, String newState, + String oldState1, String oldState2, String oldState3) + throws SQLException; + + /** + *+ * Update the all triggers to the given new state, if they are in one of + * the given old states AND its next fire time is before the given time. + *
+ * + * @param conn + * the DB connection + * @param newState + * the new state for the trigger + * @param oldState1 + * one of the old state the trigger must be in + * @param oldState2 + * one of the old state the trigger must be in + * @param time + * the time before which the trigger's next fire time must be + * @return int the number of rows updated + * @throws SQLException + */ + int updateTriggerStateFromOtherStatesBeforeTime(Connection conn, + String newState, String oldState1, String oldState2, long time) + throws SQLException; + + /** + *+ * Update all triggers in the given group to the given new state, if they + * are in one of the given old states. + *
+ * + * @param conn + * the DB connection + * @param groupName + * the group containing the trigger + * @param newState + * the new state for the trigger + * @param oldState1 + * one of the old state the trigger must be in + * @param oldState2 + * one of the old state the trigger must be in + * @param oldState3 + * one of the old state the trigger must be in + * @return int the number of rows updated + * @throws SQLException + */ + int updateTriggerGroupStateFromOtherStates(Connection conn, + String groupName, String newState, String oldState1, + String oldState2, String oldState3) throws SQLException; + + /** + *+ * Update all of the triggers of the given group to the given new state, if + * they are in the given old state. + *
+ * + * @param conn + * the DB connection + * @param groupName + * the group containing the triggers + * @param newState + * the new state for the trigger group + * @param oldState + * the old state the triggers must be in + * @return int the number of rows updated + * @throws SQLException + */ + int updateTriggerGroupStateFromOtherState(Connection conn, + String groupName, String newState, String oldState) + throws SQLException; + + /** + *+ * Update the states of all triggers associated with the given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @param state + * the new state for the triggers + * @return the number of rows updated + */ + int updateTriggerStatesForJob(Connection conn, String jobName, + String groupName, String state) throws SQLException; + + /** + *+ * Update the states of any triggers associated with the given job, that + * are the given current state. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @param state + * the new state for the triggers + * @param oldState + * the old state of the triggers + * @return the number of rows updated + */ + int updateTriggerStatesForJobFromOtherState(Connection conn, + String jobName, String groupName, String state, String oldState) + throws SQLException; + + /** + *+ * Delete all of the listeners associated with a given trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger whose listeners will be deleted + * @param groupName + * the name of the group containing the trigger + * @return the number of rows deleted + */ + int deleteTriggerListeners(Connection conn, String triggerName, + String groupName) throws SQLException; + + /** + *+ * Associate a listener with the given trigger. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger + * @param listener + * the name of the listener to associate with the trigger + * @return the number of rows inserted + */ + int insertTriggerListener(Connection conn, Trigger trigger, + String listener) throws SQLException; + + /** + *+ * Select the listeners associated with a given trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return array ofString
trigger listener names
+ */
+ String[] selectTriggerListeners(Connection conn, String triggerName,
+ String groupName) throws SQLException;
+
+ /**
+ * + * Delete the simple trigger data for a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the number of rows deleted + */ + int deleteSimpleTrigger(Connection conn, String triggerName, + String groupName) throws SQLException; + + /** + *+ * Delete the BLOB trigger data for a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the number of rows deleted + */ + int deleteBlobTrigger(Connection conn, String triggerName, + String groupName) throws SQLException; + + /** + *+ * Delete the cron trigger data for a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the number of rows deleted + */ + int deleteCronTrigger(Connection conn, String triggerName, + String groupName) throws SQLException; + + /** + *+ * Delete the base trigger data for a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the number of rows deleted + */ + int deleteTrigger(Connection conn, String triggerName, + String groupName) throws SQLException; + + /** + *+ * Select the number of triggers associated with a given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return the number of triggers for the given job + */ + int selectNumTriggersForJob(Connection conn, String jobName, + String groupName) throws SQLException; + + /** + *+ * Select the job to which the trigger is associated. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the{@link com.fr.third.org.quartz.JobDetail}
object
+ * associated with the given trigger
+ */
+ JobDetail selectJobForTrigger(Connection conn, String triggerName,
+ String groupName, ClassLoadHelper loadHelper)
+ throws ClassNotFoundException, SQLException;
+
+ /**
+ * + * Select the stateful jobs which are referenced by triggers in the given + * trigger group. + *
+ * + * @param conn + * the DB Connection + * @param groupName + * the trigger group + * @return a List of Keys to jobs. + */ + List selectStatefulJobsOfTriggerGroup(Connection conn, + String groupName) throws SQLException; + + /** + *+ * Select the triggers for a job + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return an array of(@link com.fr.third.org.quartz.Trigger)
objects
+ * associated with a given job.
+ * @throws SQLException
+ */
+ Trigger[] selectTriggersForJob(Connection conn, String jobName,
+ String groupName) throws SQLException, ClassNotFoundException,
+ IOException;
+
+ /**
+ * + * Select the triggers for a calendar + *
+ * + * @param conn + * the DB Connection + * @param calName + * the name of the calendar + * @return an array of(@link com.fr.third.org.quartz.Trigger)
objects
+ * associated with the given calendar.
+ * @throws SQLException
+ */
+ Trigger[] selectTriggersForCalendar(Connection conn, String calName)
+ throws SQLException, ClassNotFoundException, IOException;
+ /**
+ * + * Select a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the{@link com.fr.third.org.quartz.Trigger}
object
+ */
+ Trigger selectTrigger(Connection conn, String triggerName,
+ String groupName) throws SQLException, ClassNotFoundException,
+ IOException;
+
+ /**
+ * + * Select a trigger's JobDataMap. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the{@link com.fr.third.org.quartz.JobDataMap}
of the Trigger,
+ * never null, but possibly empty.
+ */
+ JobDataMap selectTriggerJobDataMap(Connection conn, String triggerName,
+ String groupName) throws SQLException, ClassNotFoundException,
+ IOException;
+
+ /**
+ * + * Select a trigger' state value. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the{@link com.fr.third.org.quartz.Trigger}
object
+ */
+ String selectTriggerState(Connection conn, String triggerName,
+ String groupName) throws SQLException;
+
+ /**
+ * + * Select a trigger' status (state & next fire time). + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return aTriggerStatus
object, or null
+ */
+ TriggerStatus selectTriggerStatus(Connection conn,
+ String triggerName, String groupName) throws SQLException;
+
+ /**
+ * + * Select the total number of triggers stored. + *
+ * + * @param conn + * the DB Connection + * @return the total number of triggers stored + */ + int selectNumTriggers(Connection conn) throws SQLException; + + /** + *+ * Select all of the trigger group names that are stored. + *
+ * + * @param conn + * the DB Connection + * @return an array ofString
group names
+ */
+ String[] selectTriggerGroups(Connection conn) throws SQLException;
+
+ /**
+ * + * Select all of the triggers contained in a given group. + *
+ * + * @param conn + * the DB Connection + * @param groupName + * the group containing the triggers + * @return an array ofString
trigger names
+ */
+ String[] selectTriggersInGroup(Connection conn, String groupName)
+ throws SQLException;
+
+ /**
+ * + * Select all of the triggers in a given state. + *
+ * + * @param conn + * the DB Connection + * @param state + * the state the triggers must be in + * @return an array of triggerKey
s
+ */
+ Key[] selectTriggersInState(Connection conn, String state)
+ throws SQLException;
+
+ int insertPausedTriggerGroup(Connection conn, String groupName)
+ throws SQLException;
+
+ int deletePausedTriggerGroup(Connection conn, String groupName)
+ throws SQLException;
+
+ int deleteAllPausedTriggerGroups(Connection conn)
+ throws SQLException;
+
+ boolean isTriggerGroupPaused(Connection conn, String groupName)
+ throws SQLException;
+
+ Set selectPausedTriggerGroups(Connection conn)
+ throws SQLException;
+
+ boolean isExistingTriggerGroup(Connection conn, String groupName)
+ throws SQLException;
+
+ //---------------------------------------------------------------------------
+ // calendars
+ //---------------------------------------------------------------------------
+
+ /**
+ * + * Insert a new calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name for the new calendar + * @param calendar + * the calendar + * @return the number of rows inserted + * @throws IOException + * if there were problems serializing the calendar + */ + int insertCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException; + + /** + *+ * Update a calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name for the new calendar + * @param calendar + * the calendar + * @return the number of rows updated + * @throws IOException + * if there were problems serializing the calendar + */ + int updateCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException; + + /** + *+ * Check whether or not a calendar exists. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name of the calendar + * @return true if the trigger exists, false otherwise + */ + boolean calendarExists(Connection conn, String calendarName) + throws SQLException; + + /** + *+ * Select a calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name of the calendar + * @return the Calendar + * @throws ClassNotFoundException + * if a class found during deserialization cannot be found be + * found + * @throws IOException + * if there were problems deserializing the calendar + */ + Calendar selectCalendar(Connection conn, String calendarName) + throws ClassNotFoundException, IOException, SQLException; + + /** + *+ * Check whether or not a calendar is referenced by any triggers. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name of the calendar + * @return true if any triggers reference the calendar, false otherwise + */ + boolean calendarIsReferenced(Connection conn, String calendarName) + throws SQLException; + + /** + *+ * Delete a calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name of the trigger + * @return the number of rows deleted + */ + int deleteCalendar(Connection conn, String calendarName) + throws SQLException; + + /** + *+ * Select the total number of calendars stored. + *
+ * + * @param conn + * the DB Connection + * @return the total number of calendars stored + */ + int selectNumCalendars(Connection conn) throws SQLException; + + /** + *+ * Select all of the stored calendars. + *
+ * + * @param conn + * the DB Connection + * @return an array ofString
calendar names
+ */
+ String[] selectCalendars(Connection conn) throws SQLException;
+
+ //---------------------------------------------------------------------------
+ // trigger firing
+ //---------------------------------------------------------------------------
+
+ /**
+ * + * Select the next time that a trigger will be fired. + *
+ * + * @param conn + * the DB Connection + * @return the next fire time, or 0 if no trigger will be fired + * + * @deprecated Does not account for misfires. + */ + long selectNextFireTime(Connection conn) throws SQLException; + + /** + *+ * Select the trigger that will be fired at the given fire time. + *
+ * + * @param conn + * the DB Connection + * @param fireTime + * the time that the trigger will be fired + * @return a{@link com.fr.third.org.quartz.utils.Key}
representing the
+ * trigger that will be fired at the given fire time, or null if no
+ * trigger will be fired at that time
+ */
+ Key selectTriggerForFireTime(Connection conn, long fireTime)
+ throws SQLException;
+
+ /**
+ * + * Select the next trigger which will fire to fire between the two given timestamps + * in ascending order of fire time, and then descending by priority. + *
+ * + * @param conn + * the DB Connection + * @param noLaterThan + * highest value ofgetNextFireTime()
of the triggers (exclusive)
+ * @param noEarlierThan
+ * highest value of getNextFireTime()
of the triggers (inclusive)
+ *
+ * @return A (never null, possibly empty) list of the identifiers (Key objects) of the next triggers to be fired.
+ */
+ List selectTriggerToAcquire(Connection conn, long noLaterThan, long noEarlierThan)
+ throws SQLException;
+
+ /**
+ * + * Insert a fired trigger. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger + * @param state + * the state that the trigger should be stored in + * @return the number of rows inserted + */ + int insertFiredTrigger(Connection conn, Trigger trigger, + String state, JobDetail jobDetail) throws SQLException; + + /** + *
+ * Select the states of all fired-trigger records for a given trigger, or
+ * trigger group if trigger name is null
.
+ *
+ * Select the states of all fired-trigger records for a given job, or job
+ * group if job name is null
.
+ *
+ * Select the states of all fired-trigger records for a given scheduler + * instance. + *
+ * + * @return a List of FiredTriggerRecord objects. + */ + List selectInstancesFiredTriggerRecords(Connection conn, + String instanceName) throws SQLException; + + + /** + *+ * Select the distinct instance names of all fired-trigger records. + *
+ * + *+ * This is useful when trying to identify orphaned fired triggers (a + * fired trigger without a scheduler state record.) + *
+ * + * @return a Set of String objects. + */ + Set selectFiredTriggerInstanceNames(Connection conn) + throws SQLException; + + /** + *+ * Delete a fired trigger. + *
+ * + * @param conn + * the DB Connection + * @param entryId + * the fired trigger entry to delete + * @return the number of rows deleted + */ + int deleteFiredTrigger(Connection conn, String entryId) + throws SQLException; + + /** + *+ * Get the number instances of the identified job currently executing. + *
+ * + * @param conn + * the DB Connection + * @return the number instances of the identified job currently executing. + */ + int selectJobExecutionCount(Connection conn, String jobName, + String jobGroup) throws SQLException; + + /** + *+ * Insert a scheduler-instance state record. + *
+ * + * @param conn + * the DB Connection + * @return the number of inserted rows. + */ + int insertSchedulerState(Connection conn, String instanceId, + long checkInTime, long interval) + throws SQLException; + + /** + *+ * Delete a scheduler-instance state record. + *
+ * + * @param conn + * the DB Connection + * @return the number of deleted rows. + */ + int deleteSchedulerState(Connection conn, String instanceId) + throws SQLException; + + + /** + *+ * Update a scheduler-instance state record. + *
+ * + * @param conn + * the DB Connection + * @return the number of updated rows. + */ + int updateSchedulerState(Connection conn, String instanceId, long checkInTime) + throws SQLException; + + /** + *
+ * A List of all current SchedulerStateRecords
.
+ *
+ * If instanceId is not null, then only the record for the identified + * instance will be returned. + *
+ * + * @param conn + * the DB Connection + */ + List selectSchedulerStateRecords(Connection conn, String instanceId) + throws SQLException; + +} + +// EOF diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/FiredTriggerRecord.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/FiredTriggerRecord.java new file mode 100644 index 000000000..05532f0c7 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/FiredTriggerRecord.java @@ -0,0 +1,154 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.impl.jdbcjobstore; + +import com.fr.third.org.quartz.utils.Key; + +/** + *+ * Conveys the state of a fired-trigger record. + *
+ * + * @author James House + */ +public class FiredTriggerRecord implements java.io.Serializable { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private String fireInstanceId; + + private long fireTimestamp; + + private String schedulerInstanceId; + + private Key triggerKey; + + private String fireInstanceState; + + private boolean triggerIsVolatile; + + private Key jobKey; + + private boolean jobIsStateful; + + private boolean jobRequestsRecovery; + + private int priority; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public String getFireInstanceId() { + return fireInstanceId; + } + + public long getFireTimestamp() { + return fireTimestamp; + } + + public boolean isJobIsStateful() { + return jobIsStateful; + } + + public Key getJobKey() { + return jobKey; + } + + public String getSchedulerInstanceId() { + return schedulerInstanceId; + } + + public Key getTriggerKey() { + return triggerKey; + } + + public String getFireInstanceState() { + return fireInstanceState; + } + + public void setFireInstanceId(String string) { + fireInstanceId = string; + } + + public void setFireTimestamp(long l) { + fireTimestamp = l; + } + + public void setJobIsStateful(boolean b) { + jobIsStateful = b; + } + + public void setJobKey(Key key) { + jobKey = key; + } + + public void setSchedulerInstanceId(String string) { + schedulerInstanceId = string; + } + + public void setTriggerKey(Key key) { + triggerKey = key; + } + + public void setFireInstanceState(String string) { + fireInstanceState = string; + } + + public boolean isJobRequestsRecovery() { + return jobRequestsRecovery; + } + + public boolean isTriggerIsVolatile() { + return triggerIsVolatile; + } + + public void setJobRequestsRecovery(boolean b) { + jobRequestsRecovery = b; + } + + public void setTriggerIsVolatile(boolean b) { + triggerIsVolatile = b; + } + + public int getPriority() { + return priority; + } + + + public void setPriority(int priority) { + this.priority = priority; + } + + +} + +// EOF diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/HSQLDBDelegate.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/HSQLDBDelegate.java new file mode 100644 index 000000000..5b5b32ce0 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/HSQLDBDelegate.java @@ -0,0 +1,122 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.impl.jdbcjobstore; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.apache.commons.logging.Log; + +/** + *+ * This is a driver delegate for the HSQLDB database. + *
+ * + * @author James House + * @author Jeffrey Wescott + */ +public class HSQLDBDelegate extends StdJDBCDelegate { + /** + *+ * Create new HSQLDBDelegate instance. + *
+ * + * @param log + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + */ + public HSQLDBDelegate(Log log, String tablePrefix, String instanceId) { + super(log, tablePrefix, instanceId); + } + + /** + *+ * Create new MSSQLDelegate instance. + *
+ * + * @param log + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + * @param useProperties + * use java.util.Properties for storage + */ + public HSQLDBDelegate(Log log, String tablePrefix, String instanceId, + Boolean useProperties) { + super(log, tablePrefix, instanceId, useProperties); + } + + //--------------------------------------------------------------------------- + // protected methods that can be overridden by subclasses + //--------------------------------------------------------------------------- + + /** + *
+ * This method should be overridden by any delegate subclasses that need
+ * special handling for BLOBs. The default implementation uses standard
+ * JDBC java.sql.Blob
operations.
+ *
+ * Exception class for when a driver delegate cannot be found for a given + * configuration, or lack thereof. + *
+ * + * @author Jeffrey Wescott + */ +public class InvalidConfigurationException extends Exception { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public InvalidConfigurationException(String msg) { + super(msg); + } + + public InvalidConfigurationException() { + super(); + } +} + +// EOF diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/JTANonClusteredSemaphore.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/JTANonClusteredSemaphore.java new file mode 100644 index 000000000..1c5b9e85c --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/JTANonClusteredSemaphore.java @@ -0,0 +1,304 @@ +/* + * Copyright 2004-2006 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +package com.fr.third.org.quartz.impl.jdbcjobstore; + +import java.sql.Connection; +import java.util.HashSet; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.transaction.Synchronization; +import javax.transaction.SystemException; +import javax.transaction.Transaction; +import javax.transaction.TransactionManager; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Provides in memory thread/resource locking that is JTA + *{@link javax.transaction.Transaction}
aware.
+ * It is most appropriate for use when using
+ * {@link com.fr.third.org.quartz.impl.jdbcjobstore.JobStoreCMT}
without clustering.
+ *
+ *
+ * This Semaphore
implementation is not Quartz cluster safe.
+ *
+ * When a lock is obtained/released but there is no active JTA
+ * {@link javax.transaction.Transaction}
, then this Semaphore
operates
+ * just like {@link com.fr.third.org.quartz.impl.jdbcjobstore.SimpleSemaphore}
.
+ *
+ * By default, this class looks for the {@link javax.transaction.TransactionManager}
+ * in JNDI under name "java:TransactionManager". If this is not where your Application Server
+ * registers it, you can modify the JNDI lookup location using the
+ * "transactionManagerJNDIName" property.
+ *
+ * IMPORTANT: This Semaphore implementation is currently experimental. + * It has been tested a limited amount on JBoss 4.0.3SP1. If you do choose to + * use it, any feedback would be most appreciated! + *
+ * + * @see com.fr.third.org.quartz.impl.jdbcjobstore.SimpleSemaphore + */ +public class JTANonClusteredSemaphore implements Semaphore { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public static final String DEFAULT_TRANSACTION_MANANGER_LOCATION = "java:TransactionManager"; + + ThreadLocal lockOwners = new ThreadLocal(); + + HashSet locks = new HashSet(); + + private final Log log = LogFactory.getLog(getClass()); + + private String transactionManagerJNDIName = DEFAULT_TRANSACTION_MANANGER_LOCATION; + + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + protected Log getLog() { + return log; + } + + public void setTransactionManagerJNDIName(String transactionManagerJNDIName) { + this.transactionManagerJNDIName = transactionManagerJNDIName; + } + + private HashSet getThreadLocks() { + HashSet threadLocks = (HashSet) lockOwners.get(); + if (threadLocks == null) { + threadLocks = new HashSet(); + lockOwners.set(threadLocks); + } + return threadLocks; + } + + /** + * Grants a lock on the identified resource to the calling thread (blocking + * until it is available). + * + * @return true if the lock was obtained. + */ + public synchronized boolean obtainLock(Connection conn, String lockName) throws LockException { + + lockName = lockName.intern(); + + Log log = getLog(); + + if(log.isDebugEnabled()) { + log.debug( + "Lock '" + lockName + "' is desired by: " + + Thread.currentThread().getName()); + } + + if (!isLockOwner(conn, lockName)) { + if(log.isDebugEnabled()) { + log.debug( + "Lock '" + lockName + "' is being obtained: " + + Thread.currentThread().getName()); + } + + while (locks.contains(lockName)) { + try { + this.wait(); + } catch (InterruptedException ie) { + if(log.isDebugEnabled()) { + log.debug( + "Lock '" + lockName + "' was not obtained by: " + + Thread.currentThread().getName()); + } + } + } + + // If we are in a transaction, register a callback to actually release + // the lock when the transaction completes + Transaction t = getTransaction(); + if (t != null) { + try { + t.registerSynchronization(new SemaphoreSynchronization(lockName)); + } catch (Exception e) { + throw new LockException("Failed to register semaphore with Transaction.", e); + } + } + + if(log.isDebugEnabled()) { + log.debug( + "Lock '" + lockName + "' given to: " + + Thread.currentThread().getName()); + } + + + getThreadLocks().add(lockName); + locks.add(lockName); + } else if(log.isDebugEnabled()) { + log.debug( + "Lock '" + lockName + "' already owned by: " + + Thread.currentThread().getName() + + " -- but not owner!", + new Exception("stack-trace of wrongful returner")); + } + + return true; + } + + /** + * Helper method to get the current{@link javax.transaction.Transaction}
+ * from the {@link javax.transaction.TransactionManager}
in JNDI.
+ *
+ * @return The current {@link javax.transaction.Transaction}
, null if
+ * not currently in a transaction.
+ */
+ protected Transaction getTransaction() throws LockException{
+ InitialContext ic = null;
+ try {
+ ic = new InitialContext();
+ TransactionManager tm = (TransactionManager)ic.lookup(transactionManagerJNDIName);
+
+ return tm.getTransaction();
+ } catch (SystemException e) {
+ throw new LockException("Failed to get Transaction from TransactionManager", e);
+ } catch (NamingException e) {
+ throw new LockException("Failed to find TransactionManager in JNDI under name: " + transactionManagerJNDIName, e);
+ } finally {
+ if (ic != null) {
+ try {
+ ic.close();
+ } catch (NamingException ignored) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Release the lock on the identified resource if it is held by the calling
+ * thread, unless currently in a JTA transaction.
+ */
+ public synchronized void releaseLock(Connection conn, String lockName) throws LockException {
+ releaseLock(lockName, false);
+ }
+
+ /**
+ * Release the lock on the identified resource if it is held by the calling
+ * thread, unless currently in a JTA transaction.
+ *
+ * @param fromSynchronization True if this method is being invoked from
+ * {@link Synchronization}
notified of the enclosing
+ * transaction having completed.
+ *
+ * @throws LockException Thrown if there was a problem accessing the JTA
+ * Transaction
. Only relevant if fromSynchronization
+ * is false.
+ */
+ protected synchronized void releaseLock(
+ String lockName, boolean fromSynchronization) throws LockException {
+ lockName = lockName.intern();
+
+ if (isLockOwner(null, lockName)) {
+
+ if (fromSynchronization == false) {
+ Transaction t = getTransaction();
+ if (t != null) {
+ if(getLog().isDebugEnabled()) {
+ getLog().debug(
+ "Lock '" + lockName + "' is in a JTA transaction. " +
+ "Return deferred by: " + Thread.currentThread().getName());
+ }
+
+ // If we are still in a transaction, then we don't want to
+ // actually release the lock.
+ return;
+ }
+ }
+
+ if(getLog().isDebugEnabled()) {
+ getLog().debug(
+ "Lock '" + lockName + "' returned by: "
+ + Thread.currentThread().getName());
+ }
+ getThreadLocks().remove(lockName);
+ locks.remove(lockName);
+ this.notify();
+ } else if (getLog().isDebugEnabled()) {
+ getLog().debug(
+ "Lock '" + lockName + "' attempt to return by: "
+ + Thread.currentThread().getName()
+ + " -- but not owner!",
+ new Exception("stack-trace of wrongful returner"));
+ }
+ }
+
+ /**
+ * Determine whether the calling thread owns a lock on the identified
+ * resource.
+ */
+ public synchronized boolean isLockOwner(Connection conn, String lockName) {
+ lockName = lockName.intern();
+
+ return getThreadLocks().contains(lockName);
+ }
+
+ /**
+ * This Semaphore implementation does not use the database.
+ */
+ public boolean requiresConnection() {
+ return false;
+ }
+
+ /**
+ * Helper class that is registered with the active
+ * {@link javax.transaction.Transaction}
so that the lock
+ * will be released when the transaction completes.
+ */
+ private class SemaphoreSynchronization implements Synchronization {
+ private String lockName;
+
+ public SemaphoreSynchronization(String lockName) {
+ this.lockName = lockName;
+ }
+
+ public void beforeCompletion() {
+ // nothing to do...
+ }
+
+ public void afterCompletion(int status) {
+ try {
+ releaseLock(lockName, true);
+ } catch (LockException e) {
+ // Ignore as can't be thrown with fromSynchronization set to true
+ }
+ }
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/JobStoreCMT.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/JobStoreCMT.java
new file mode 100644
index 000000000..da194bc76
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/JobStoreCMT.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import com.fr.third.org.quartz.JobPersistenceException;
+import com.fr.third.org.quartz.SchedulerConfigException;
+import com.fr.third.org.quartz.spi.ClassLoadHelper;
+import com.fr.third.org.quartz.spi.SchedulerSignaler;
+import com.fr.third.org.quartz.utils.DBConnectionManager;
+
+/**
+ *
+ * JobStoreCMT
is meant to be used in an application-server
+ * environment that provides container-managed-transactions. No commit /
+ * rollback will be1 handled by this class.
+ *
+ * If you need commit / rollback, use {@link
+ * com.fr.third.org.quartz.impl.jdbcjobstore.JobStoreTX}
+ * instead.
+ *
+ * Set the name of the DataSource
that should be used for
+ * performing database functions.
+ *
+ * Get the name of the DataSource
that should be used for
+ * performing database functions.
+ *
+ * Contains base functionality for JDBC-based JobStore implementations. + *
+ * + * @author Jeffrey Wescott + * @author James House + */ +public abstract class JobStoreSupport implements JobStore, Constants { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + protected static final String LOCK_TRIGGER_ACCESS = "TRIGGER_ACCESS"; + + protected static final String LOCK_JOB_ACCESS = "JOB_ACCESS"; + + protected static final String LOCK_CALENDAR_ACCESS = "CALENDAR_ACCESS"; + + protected static final String LOCK_STATE_ACCESS = "STATE_ACCESS"; + + protected static final String LOCK_MISFIRE_ACCESS = "MISFIRE_ACCESS"; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + protected String dsName; + + protected String tablePrefix = DEFAULT_TABLE_PREFIX; + + protected boolean useProperties = false; + + protected String instanceId; + + protected String instanceName; + + protected String delegateClassName; + protected Class delegateClass = StdJDBCDelegate.class; + + protected HashMap calendarCache = new HashMap(); + + private DriverDelegate delegate; + + private long misfireThreshold = 60000L; // one minute + + private boolean dontSetAutoCommitFalse = false; + + private boolean isClustered = false; + + private boolean useDBLocks = false; + + private boolean lockOnInsert = true; + + private Semaphore lockHandler = null; // set in initialize() method... + + private String selectWithLockSQL = null; + + private long clusterCheckinInterval = 7500L; + + private ClusterManager clusterManagementThread = null; + + private MisfireHandler misfireHandler = null; + + private ClassLoadHelper classLoadHelper; + + private SchedulerSignaler signaler; + + protected int maxToRecoverAtATime = 20; + + private boolean setTxIsolationLevelSequential = false; + + private boolean acquireTriggersWithinLock = false; + + private long dbRetryInterval = 10000; + + private boolean makeThreadsDaemons = false; + + private boolean threadsInheritInitializersClassLoadContext = false; + private ClassLoader initializersLoader = null; + + private boolean doubleCheckLockMisfireHandler = true; + + private final Log log = LogFactory.getLog(getClass()); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *
+ * Set the name of the DataSource
that should be used for
+ * performing database functions.
+ *
+ * Get the name of the DataSource
that should be used for
+ * performing database functions.
+ *
+ * Set the prefix that should be pre-pended to all table names. + *
+ */ + public void setTablePrefix(String prefix) { + if (prefix == null) { + prefix = ""; + } + + this.tablePrefix = prefix; + } + + /** + *+ * Get the prefix that should be pre-pended to all table names. + *
+ */ + public String getTablePrefix() { + return tablePrefix; + } + + /** + *+ * Set whether String-only properties will be handled in JobDataMaps. + *
+ */ + public void setUseProperties(String useProp) { + if (useProp == null) { + useProp = "false"; + } + + this.useProperties = Boolean.valueOf(useProp).booleanValue(); + } + + /** + *+ * Get whether String-only properties will be handled in JobDataMaps. + *
+ */ + public boolean canUseProperties() { + return useProperties; + } + + /** + *+ * Set the instance Id of the Scheduler (must be unique within a cluster). + *
+ */ + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + /** + *+ * Get the instance Id of the Scheduler (must be unique within a cluster). + *
+ */ + public String getInstanceId() { + + return instanceId; + } + + /** + * Set the instance name of the Scheduler (must be unique within this server instance). + */ + public void setInstanceName(String instanceName) { + this.instanceName = instanceName; + } + + /** + * Get the instance name of the Scheduler (must be unique within this server instance). + */ + public String getInstanceName() { + + return instanceName; + } + + /** + *+ * Set whether this instance is part of a cluster. + *
+ */ + public void setIsClustered(boolean isClustered) { + this.isClustered = isClustered; + } + + /** + *+ * Get whether this instance is part of a cluster. + *
+ */ + public boolean isClustered() { + return isClustered; + } + + /** + *+ * Get the frequency (in milliseconds) at which this instance "checks-in" + * with the other instances of the cluster. -- Affects the rate of + * detecting failed instances. + *
+ */ + public long getClusterCheckinInterval() { + return clusterCheckinInterval; + } + + /** + *+ * Set the frequency (in milliseconds) at which this instance "checks-in" + * with the other instances of the cluster. -- Affects the rate of + * detecting failed instances. + *
+ */ + public void setClusterCheckinInterval(long l) { + clusterCheckinInterval = l; + } + + /** + *+ * Get the maximum number of misfired triggers that the misfire handling + * thread will try to recover at one time (within one transaction). The + * default is 20. + *
+ */ + public int getMaxMisfiresToHandleAtATime() { + return maxToRecoverAtATime; + } + + /** + *+ * Set the maximum number of misfired triggers that the misfire handling + * thread will try to recover at one time (within one transaction). The + * default is 20. + *
+ */ + public void setMaxMisfiresToHandleAtATime(int maxToRecoverAtATime) { + this.maxToRecoverAtATime = maxToRecoverAtATime; + } + + /** + * @return Returns the dbRetryInterval. + */ + public long getDbRetryInterval() { + return dbRetryInterval; + } + /** + * @param dbRetryInterval The dbRetryInterval to set. + */ + public void setDbRetryInterval(long dbRetryInterval) { + this.dbRetryInterval = dbRetryInterval; + } + + /** + *+ * Set whether this instance should use database-based thread + * synchronization. + *
+ */ + public void setUseDBLocks(boolean useDBLocks) { + this.useDBLocks = useDBLocks; + } + + /** + *+ * Get whether this instance should use database-based thread + * synchronization. + *
+ */ + public boolean getUseDBLocks() { + return useDBLocks; + } + + public boolean isLockOnInsert() { + return lockOnInsert; + } + + /** + * Whether or not to obtain locks when inserting new jobs/triggers. + * Defaults totrue
, which is safest - some db's (such as
+ * MS SQLServer) seem to require this to avoid deadlocks under high load,
+ * while others seem to do fine without.
+ *
+ * Setting this property to false
will provide a
+ * significant performance increase during the addition of new jobs
+ * and triggers.
+ * Set the JDBC driver delegate class. + *
+ * + * @param delegateClassName + * the delegate class name + */ + public void setDriverDelegateClass(String delegateClassName) + throws InvalidConfigurationException { + this.delegateClassName = delegateClassName; + } + + /** + *+ * Get the JDBC driver delegate class name. + *
+ * + * @return the delegate class name + */ + public String getDriverDelegateClass() { + return delegateClassName; + } + + public String getSelectWithLockSQL() { + return selectWithLockSQL; + } + + /** + *+ * set the SQL statement to use to select and lock a row in the "locks" + * table. + *
+ * + * @see StdRowLockSemaphore + */ + public void setSelectWithLockSQL(String string) { + selectWithLockSQL = string; + } + + protected ClassLoadHelper getClassLoadHelper() { + return classLoadHelper; + } + + /** + * Get whether the threads spawned by this JobStore should be + * marked as daemon. Possible threads include theMisfireHandler
+ * and the ClusterManager
.
+ *
+ * @see Thread#setDaemon(boolean)
+ */
+ public boolean getMakeThreadsDaemons() {
+ return makeThreadsDaemons;
+ }
+
+ /**
+ * Set whether the threads spawned by this JobStore should be
+ * marked as daemon. Possible threads include the MisfireHandler
+ * and the ClusterManager
.
+ *
+ * @see Thread#setDaemon(boolean)
+ */
+ public void setMakeThreadsDaemons(boolean makeThreadsDaemons) {
+ this.makeThreadsDaemons = makeThreadsDaemons;
+ }
+
+ /**
+ * Get whether to set the class load context of spawned threads to that
+ * of the initializing thread.
+ */
+ public boolean isThreadsInheritInitializersClassLoadContext() {
+ return threadsInheritInitializersClassLoadContext;
+ }
+
+ /**
+ * Set whether to set the class load context of spawned threads to that
+ * of the initializing thread.
+ */
+ public void setThreadsInheritInitializersClassLoadContext(
+ boolean threadsInheritInitializersClassLoadContext) {
+ this.threadsInheritInitializersClassLoadContext = threadsInheritInitializersClassLoadContext;
+ }
+
+ /**
+ * Get whether to check to see if there are Triggers that have misfired
+ * before actually acquiring the lock to recover them. This should be
+ * set to false if the majority of the time, there are are misfired
+ * Triggers.
+ */
+ public boolean getDoubleCheckLockMisfireHandler() {
+ return doubleCheckLockMisfireHandler;
+ }
+
+ /**
+ * Set whether to check to see if there are Triggers that have misfired
+ * before actually acquiring the lock to recover them. This should be
+ * set to false if the majority of the time, there are are misfired
+ * Triggers.
+ */
+ public void setDoubleCheckLockMisfireHandler(
+ boolean doubleCheckLockMisfireHandler) {
+ this.doubleCheckLockMisfireHandler = doubleCheckLockMisfireHandler;
+ }
+
+ //---------------------------------------------------------------------------
+ // interface methods
+ //---------------------------------------------------------------------------
+
+ protected Log getLog() {
+ return log;
+ }
+
+ /**
+ *
+ * Called by the QuartzScheduler before the JobStore
is
+ * used, in order to give it a chance to initialize.
+ *
+ * Called by the QuartzScheduler to inform the JobStore
that
+ * it should free up all of it's resources because the scheduler is
+ * shutting down.
+ *
Connection
in a Proxy such that attributes
+ * that might be set will be restored before the connection is closed
+ * (and potentially restored to a pool).
+ */
+ protected Connection getAttributeRestoringConnection(Connection conn) {
+ return (Connection)Proxy.newProxyInstance(
+ Thread.currentThread().getContextClassLoader(),
+ new Class[] { Connection.class },
+ new AttributeRestoringConnectionInvocationHandler(conn));
+ }
+
+ protected Connection getConnection() throws JobPersistenceException {
+ Connection conn = null;
+ try {
+ conn = DBConnectionManager.getInstance().getConnection(
+ getDataSource());
+ } catch (SQLException sqle) {
+ throw new JobPersistenceException(
+ "Failed to obtain DB connection from data source '"
+ + getDataSource() + "': " + sqle.toString(), sqle);
+ } catch (Throwable e) {
+ throw new JobPersistenceException(
+ "Failed to obtain DB connection from data source '"
+ + getDataSource() + "': " + e.toString(), e,
+ JobPersistenceException.ERR_PERSISTENCE_CRITICAL_FAILURE);
+ }
+
+ if (conn == null) {
+ throw new JobPersistenceException(
+ "Could not get connection from DataSource '"
+ + getDataSource() + "'");
+ }
+
+ // Protect connection attributes we might change.
+ conn = getAttributeRestoringConnection(conn);
+
+ // Set any connection connection attributes we are to override.
+ try {
+ if (!isDontSetAutoCommitFalse()) {
+ conn.setAutoCommit(false);
+ }
+
+ if(isTxIsolationLevelSerializable()) {
+ conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
+ }
+ } catch (SQLException sqle) {
+ getLog().warn("Failed to override connection auto commit/transaction isolation.", sqle);
+ } catch (Throwable e) {
+ try { conn.close(); } catch(Throwable tt) {}
+
+ throw new JobPersistenceException(
+ "Failure setting up connection.", e);
+ }
+
+ return conn;
+ }
+
+ protected void releaseLock(Connection conn, String lockName, boolean doIt) {
+ if (doIt && conn != null) {
+ try {
+ getLockHandler().releaseLock(conn, lockName);
+ } catch (LockException le) {
+ getLog().error("Error returning lock: " + le.getMessage(), le);
+ }
+ }
+ }
+
+ /**
+ * Removes all volatile data.
+ *
+ * @throws JobPersistenceException If jobs could not be recovered.
+ */
+ protected void cleanVolatileTriggerAndJobs()
+ throws JobPersistenceException {
+ executeInNonManagedTXLock(
+ LOCK_TRIGGER_ACCESS,
+ new VoidTransactionCallback() {
+ public void execute(Connection conn) throws JobPersistenceException {
+ cleanVolatileTriggerAndJobs(conn);
+ }
+ });
+ }
+
+ /**
+ * + * Removes all volatile data. + *
+ * + * @throws JobPersistenceException + * if jobs could not be recovered + */ + protected void cleanVolatileTriggerAndJobs(Connection conn) + throws JobPersistenceException { + try { + // find volatile jobs & triggers... + Key[] volatileTriggers = getDelegate().selectVolatileTriggers(conn); + Key[] volatileJobs = getDelegate().selectVolatileJobs(conn); + + for (int i = 0; i < volatileTriggers.length; i++) { + removeTrigger(conn, null, volatileTriggers[i].getName(), + volatileTriggers[i].getGroup()); + } + getLog().info( + "Removed " + volatileTriggers.length + + " Volatile Trigger(s)."); + + for (int i = 0; i < volatileJobs.length; i++) { + removeJob(conn, null, volatileJobs[i].getName(), + volatileJobs[i].getGroup(), true); + } + getLog().info( + "Removed " + volatileJobs.length + " Volatile Job(s)."); + + // clean up any fired trigger entries + getDelegate().deleteVolatileFiredTriggers(conn); + + } catch (Exception e) { + throw new JobPersistenceException("Couldn't clean volatile data: " + + e.getMessage(), e); + } + } + + /** + * Recover any failed or misfired jobs and clean up the data store as + * appropriate. + * + * @throws JobPersistenceException if jobs could not be recovered + */ + protected void recoverJobs() throws JobPersistenceException { + executeInNonManagedTXLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void execute(Connection conn) throws JobPersistenceException { + recoverJobs(conn); + } + }); + } + + /** + *+ * Will recover any failed or misfired jobs and clean up the data store as + * appropriate. + *
+ * + * @throws JobPersistenceException + * if jobs could not be recovered + */ + protected void recoverJobs(Connection conn) throws JobPersistenceException { + try { + // update inconsistent job states + int rows = getDelegate().updateTriggerStatesFromOtherStates(conn, + STATE_WAITING, STATE_ACQUIRED, STATE_BLOCKED); + + rows += getDelegate().updateTriggerStatesFromOtherStates(conn, + STATE_PAUSED, STATE_PAUSED_BLOCKED, STATE_PAUSED_BLOCKED); + + getLog().info( + "Freed " + rows + + " triggers from 'acquired' / 'blocked' state."); + + // clean up misfired jobs + recoverMisfiredJobs(conn, true); + + // recover jobs marked for recovery that were not fully executed + Trigger[] recoveringJobTriggers = getDelegate() + .selectTriggersForRecoveringJobs(conn); + getLog() + .info( + "Recovering " + + recoveringJobTriggers.length + + " jobs that were in-progress at the time of the last shut-down."); + + for (int i = 0; i < recoveringJobTriggers.length; ++i) { + if (jobExists(conn, recoveringJobTriggers[i].getJobName(), + recoveringJobTriggers[i].getJobGroup())) { + recoveringJobTriggers[i].computeFirstFireTime(null); + storeTrigger(conn, null, recoveringJobTriggers[i], null, false, + STATE_WAITING, false, true); + } + } + getLog().info("Recovery complete."); + + // remove lingering 'complete' triggers... + Key[] ct = getDelegate().selectTriggersInState(conn, STATE_COMPLETE); + for(int i=0; ct != null && i < ct.length; i++) { + removeTrigger(conn, null, ct[i].getName(), ct[i].getGroup()); + } + getLog().info( + "Removed " + (ct != null ? ct.length : 0) + + " 'complete' triggers."); + + // clean up any fired trigger entries + int n = getDelegate().deleteFiredTriggers(conn); + getLog().info("Removed " + n + " stale fired job entries."); + } catch (JobPersistenceException e) { + throw e; + } catch (Exception e) { + throw new JobPersistenceException("Couldn't recover jobs: " + + e.getMessage(), e); + } + } + + protected long getMisfireTime() { + long misfireTime = System.currentTimeMillis(); + if (getMisfireThreshold() > 0) { + misfireTime -= getMisfireThreshold(); + } + + return (misfireTime > 0) ? misfireTime : 0; + } + + /** + * Helper class for returning the composite result of trying + * to recover misfired jobs. + */ + protected static class RecoverMisfiredJobsResult { + public static final RecoverMisfiredJobsResult NO_OP = + new RecoverMisfiredJobsResult(false, 0, Long.MAX_VALUE); + + private boolean _hasMoreMisfiredTriggers; + private int _processedMisfiredTriggerCount; + private long _earliestNewTime; + + public RecoverMisfiredJobsResult( + boolean hasMoreMisfiredTriggers, int processedMisfiredTriggerCount, long earliestNewTime) { + _hasMoreMisfiredTriggers = hasMoreMisfiredTriggers; + _processedMisfiredTriggerCount = processedMisfiredTriggerCount; + _earliestNewTime = earliestNewTime; + } + + public boolean hasMoreMisfiredTriggers() { + return _hasMoreMisfiredTriggers; + } + public int getProcessedMisfiredTriggerCount() { + return _processedMisfiredTriggerCount; + } + public long getEarliestNewTime() { + return _earliestNewTime; + } + } + + protected RecoverMisfiredJobsResult recoverMisfiredJobs( + Connection conn, boolean recovering) + throws JobPersistenceException, SQLException { + + // If recovering, we want to handle all of the misfired + // triggers right away. + int maxMisfiresToHandleAtATime = + (recovering) ? -1 : getMaxMisfiresToHandleAtATime(); + + List misfiredTriggers = new ArrayList(); + long earliestNewTime = Long.MAX_VALUE; + // We must still look for the MISFIRED state in case triggers were left + // in this state when upgrading to this version that does not support it. + boolean hasMoreMisfiredTriggers = + getDelegate().selectMisfiredTriggersInStates( + conn, STATE_MISFIRED, STATE_WAITING, getMisfireTime(), + maxMisfiresToHandleAtATime, misfiredTriggers); + + if (hasMoreMisfiredTriggers) { + getLog().info( + "Handling the first " + misfiredTriggers.size() + + " triggers that missed their scheduled fire-time. " + + "More misfired triggers remain to be processed."); + } else if (misfiredTriggers.size() > 0) { + getLog().info( + "Handling " + misfiredTriggers.size() + + " trigger(s) that missed their scheduled fire-time."); + } else { + getLog().debug( + "Found 0 triggers that missed their scheduled fire-time."); + return RecoverMisfiredJobsResult.NO_OP; + } + + for (Iterator misfiredTriggerIter = misfiredTriggers.iterator(); misfiredTriggerIter.hasNext();) { + Key triggerKey = (Key) misfiredTriggerIter.next(); + + Trigger trig = + retrieveTrigger(conn, triggerKey.getName(), triggerKey.getGroup()); + + if (trig == null) { + continue; + } + + doUpdateOfMisfiredTrigger(conn, null, trig, false, STATE_WAITING, recovering); + + if(trig.getNextFireTime() != null && trig.getNextFireTime().getTime() < earliestNewTime) + earliestNewTime = trig.getNextFireTime().getTime(); + + signaler.notifyTriggerListenersMisfired(trig); + } + + return new RecoverMisfiredJobsResult( + hasMoreMisfiredTriggers, misfiredTriggers.size(), earliestNewTime); + } + + protected boolean updateMisfiredTrigger(Connection conn, + SchedulingContext ctxt, String triggerName, String groupName, + String newStateIfNotComplete, boolean forceState) // TODO: probably + // get rid of + // this + throws JobPersistenceException { + try { + + Trigger trig = getDelegate().selectTrigger(conn, triggerName, + groupName); + + long misfireTime = System.currentTimeMillis(); + if (getMisfireThreshold() > 0) { + misfireTime -= getMisfireThreshold(); + } + + if (trig.getNextFireTime().getTime() > misfireTime) { + return false; + } + + doUpdateOfMisfiredTrigger(conn, ctxt, trig, forceState, newStateIfNotComplete, false); + + signaler.notifySchedulerListenersFinalized(trig); + + return true; + + } catch (Exception e) { + throw new JobPersistenceException( + "Couldn't update misfired trigger '" + groupName + "." + + triggerName + "': " + e.getMessage(), e); + } + } + + private void doUpdateOfMisfiredTrigger(Connection conn, SchedulingContext ctxt, Trigger trig, boolean forceState, String newStateIfNotComplete, boolean recovering) throws JobPersistenceException { + Calendar cal = null; + if (trig.getCalendarName() != null) { + cal = retrieveCalendar(conn, ctxt, trig.getCalendarName()); + } + + signaler.notifyTriggerListenersMisfired(trig); + + trig.updateAfterMisfire(cal); + + if (trig.getNextFireTime() == null) { + storeTrigger(conn, ctxt, trig, + null, true, STATE_COMPLETE, forceState, recovering); + } else { + storeTrigger(conn, ctxt, trig, null, true, newStateIfNotComplete, + forceState, false); + } + } + + /** + *
+ * Store the given {@link com.fr.third.org.quartz.JobDetail}
and {@link com.fr.third.org.quartz.Trigger}
.
+ *
JobDetail
to be stored.
+ * @param newTrigger
+ * The Trigger
to be stored.
+ * @throws ObjectAlreadyExistsException
+ * if a Job
with the same name/group already
+ * exists.
+ */
+ public void storeJobAndTrigger(final SchedulingContext ctxt, final JobDetail newJob,
+ final Trigger newTrigger)
+ throws ObjectAlreadyExistsException, JobPersistenceException {
+ executeInLock(
+ (isLockOnInsert()) ? LOCK_TRIGGER_ACCESS : null,
+ new VoidTransactionCallback() {
+ public void execute(Connection conn) throws JobPersistenceException {
+ if (newJob.isVolatile() && !newTrigger.isVolatile()) {
+ JobPersistenceException jpe =
+ new JobPersistenceException(
+ "Cannot associate non-volatile trigger with a volatile job!");
+ jpe.setErrorCode(SchedulerException.ERR_CLIENT_ERROR);
+ throw jpe;
+ }
+
+ storeJob(conn, ctxt, newJob, false);
+ storeTrigger(conn, ctxt, newTrigger, newJob, false,
+ Constants.STATE_WAITING, false, false);
+ }
+ });
+ }
+
+ /**
+ *
+ * Store the given {@link com.fr.third.org.quartz.JobDetail}
.
+ *
JobDetail
to be stored.
+ * @param replaceExisting
+ * If true
, any Job
existing in the
+ * JobStore
with the same name & group should be
+ * over-written.
+ * @throws ObjectAlreadyExistsException
+ * if a Job
with the same name/group already
+ * exists, and replaceExisting is set to false.
+ */
+ public void storeJob(final SchedulingContext ctxt, final JobDetail newJob,
+ final boolean replaceExisting) throws ObjectAlreadyExistsException, JobPersistenceException {
+ executeInLock(
+ (isLockOnInsert() || replaceExisting) ? LOCK_TRIGGER_ACCESS : null,
+ new VoidTransactionCallback() {
+ public void execute(Connection conn) throws JobPersistenceException {
+ storeJob(conn, ctxt, newJob, replaceExisting);
+ }
+ });
+ }
+
+ /**
+ * + * Insert or update a job. + *
+ */ + protected void storeJob(Connection conn, SchedulingContext ctxt, + JobDetail newJob, boolean replaceExisting) + throws ObjectAlreadyExistsException, JobPersistenceException { + if (newJob.isVolatile() && isClustered()) { + getLog().info( + "note: volatile jobs are effectively non-volatile in a clustered environment."); + } + + boolean existingJob = jobExists(conn, newJob.getName(), newJob + .getGroup()); + try { + if (existingJob) { + if (!replaceExisting) { + throw new ObjectAlreadyExistsException(newJob); + } + getDelegate().updateJobDetail(conn, newJob); + } else { + getDelegate().insertJobDetail(conn, newJob); + } + } catch (IOException e) { + throw new JobPersistenceException("Couldn't store job: " + + e.getMessage(), e); + } catch (SQLException e) { + throw new JobPersistenceException("Couldn't store job: " + + e.getMessage(), e); + } + } + + /** + *+ * Check existence of a given job. + *
+ */ + protected boolean jobExists(Connection conn, String jobName, + String groupName) throws JobPersistenceException { + try { + return getDelegate().jobExists(conn, jobName, groupName); + } catch (SQLException e) { + throw new JobPersistenceException( + "Couldn't determine job existence (" + groupName + "." + + jobName + "): " + e.getMessage(), e); + } + } + + + /** + *
+ * Store the given {@link com.fr.third.org.quartz.Trigger}
.
+ *
Trigger
to be stored.
+ * @param replaceExisting
+ * If true
, any Trigger
existing in
+ * the JobStore
with the same name & group should
+ * be over-written.
+ * @throws ObjectAlreadyExistsException
+ * if a Trigger
with the same name/group already
+ * exists, and replaceExisting is set to false.
+ */
+ public void storeTrigger(final SchedulingContext ctxt, final Trigger newTrigger,
+ final boolean replaceExisting) throws ObjectAlreadyExistsException,
+ JobPersistenceException {
+ executeInLock(
+ (isLockOnInsert() || replaceExisting) ? LOCK_TRIGGER_ACCESS : null,
+ new VoidTransactionCallback() {
+ public void execute(Connection conn) throws JobPersistenceException {
+ storeTrigger(conn, ctxt, newTrigger, null, replaceExisting,
+ STATE_WAITING, false, false);
+ }
+ });
+ }
+
+ /**
+ * + * Insert or update a trigger. + *
+ */ + protected void storeTrigger(Connection conn, SchedulingContext ctxt, + Trigger newTrigger, JobDetail job, boolean replaceExisting, String state, + boolean forceState, boolean recovering) + throws ObjectAlreadyExistsException, JobPersistenceException { + if (newTrigger.isVolatile() && isClustered()) { + getLog().info( + "note: volatile triggers are effectively non-volatile in a clustered environment."); + } + + boolean existingTrigger = triggerExists(conn, newTrigger.getName(), + newTrigger.getGroup()); + + if ((existingTrigger) && (!replaceExisting)) { + throw new ObjectAlreadyExistsException(newTrigger); + } + + try { + + boolean shouldBepaused = false; + + if (!forceState) { + shouldBepaused = getDelegate().isTriggerGroupPaused( + conn, newTrigger.getGroup()); + + if(!shouldBepaused) { + shouldBepaused = getDelegate().isTriggerGroupPaused(conn, + ALL_GROUPS_PAUSED); + + if (shouldBepaused) { + getDelegate().insertPausedTriggerGroup(conn, newTrigger.getGroup()); + } + } + + if (shouldBepaused && (state.equals(STATE_WAITING) || state.equals(STATE_ACQUIRED))) { + state = STATE_PAUSED; + } + } + + if(job == null) { + job = getDelegate().selectJobDetail(conn, + newTrigger.getJobName(), newTrigger.getJobGroup(), + getClassLoadHelper()); + } + if (job == null) { + throw new JobPersistenceException("The job (" + + newTrigger.getFullJobName() + + ") referenced by the trigger does not exist."); + } + if (job.isVolatile() && !newTrigger.isVolatile()) { + throw new JobPersistenceException( + "It does not make sense to " + + "associate a non-volatile Trigger with a volatile Job!"); + } + + if (job.isStateful() && !recovering) { + state = checkBlockedState(conn, ctxt, job.getName(), + job.getGroup(), state); + } + + if (existingTrigger) { + if (newTrigger instanceof SimpleTrigger && ((SimpleTrigger)newTrigger).hasAdditionalProperties() == false ) { + getDelegate().updateSimpleTrigger(conn, + (SimpleTrigger) newTrigger); + } else if (newTrigger instanceof CronTrigger && ((CronTrigger)newTrigger).hasAdditionalProperties() == false ) { + getDelegate().updateCronTrigger(conn, + (CronTrigger) newTrigger); + } else { + getDelegate().updateBlobTrigger(conn, newTrigger); + } + getDelegate().updateTrigger(conn, newTrigger, state, job); + } else { + getDelegate().insertTrigger(conn, newTrigger, state, job); + if (newTrigger instanceof SimpleTrigger && ((SimpleTrigger)newTrigger).hasAdditionalProperties() == false ) { + getDelegate().insertSimpleTrigger(conn, + (SimpleTrigger) newTrigger); + } else if (newTrigger instanceof CronTrigger && ((CronTrigger)newTrigger).hasAdditionalProperties() == false ) { + getDelegate().insertCronTrigger(conn, + (CronTrigger) newTrigger); + } else { + getDelegate().insertBlobTrigger(conn, newTrigger); + } + } + } catch (Exception e) { + throw new JobPersistenceException("Couldn't store trigger '" + newTrigger.getName() + "' for '" + + newTrigger.getJobName() + "' job:" + e.getMessage(), e); + } + } + + /** + *+ * Check existence of a given trigger. + *
+ */ + protected boolean triggerExists(Connection conn, String triggerName, + String groupName) throws JobPersistenceException { + try { + return getDelegate().triggerExists(conn, triggerName, groupName); + } catch (SQLException e) { + throw new JobPersistenceException( + "Couldn't determine trigger existence (" + groupName + "." + + triggerName + "): " + e.getMessage(), e); + } + } + + /** + *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Job}
with the given
+ * name, and any {@link com.fr.third.org.quartz.Trigger}
s that reference
+ * it.
+ *
+ * If removal of the Job
results in an empty group, the
+ * group should be removed from the JobStore
's list of
+ * known group names.
+ *
Job
to be removed.
+ * @param groupName
+ * The group name of the Job
to be removed.
+ * @return true
if a Job
with the given name &
+ * group was found and removed from the store.
+ */
+ public boolean removeJob(final SchedulingContext ctxt, final String jobName,
+ final String groupName) throws JobPersistenceException {
+ return ((Boolean)executeInLock(
+ LOCK_TRIGGER_ACCESS,
+ new TransactionCallback() {
+ public Object execute(Connection conn) throws JobPersistenceException {
+ return removeJob(conn, ctxt, jobName, groupName, true) ?
+ Boolean.TRUE : Boolean.FALSE;
+ }
+ })).booleanValue();
+ }
+
+ protected boolean removeJob(Connection conn, SchedulingContext ctxt,
+ String jobName, String groupName, boolean activeDeleteSafe)
+ throws JobPersistenceException {
+
+ try {
+ Key[] jobTriggers = getDelegate().selectTriggerNamesForJob(conn,
+ jobName, groupName);
+ for (int i = 0; i < jobTriggers.length; ++i) {
+ deleteTriggerAndChildren(
+ conn, jobTriggers[i].getName(), jobTriggers[i].getGroup());
+ }
+
+ return deleteJobAndChildren(conn, ctxt, jobName, groupName);
+ } catch (SQLException e) {
+ throw new JobPersistenceException("Couldn't remove job: "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Delete a job and its listeners.
+ *
+ * @see #removeJob(Connection, SchedulingContext, String, String, boolean)
+ * @see #removeTrigger(Connection, SchedulingContext, String, String)
+ */
+ private boolean deleteJobAndChildren(Connection conn,
+ SchedulingContext ctxt, String jobName, String groupName)
+ throws NoSuchDelegateException, SQLException {
+ getDelegate().deleteJobListeners(conn, jobName, groupName);
+
+ return (getDelegate().deleteJobDetail(conn, jobName, groupName) > 0);
+ }
+
+ /**
+ * Delete a trigger, its listeners, and its Simple/Cron/BLOB sub-table entry.
+ *
+ * @see #removeJob(Connection, SchedulingContext, String, String, boolean)
+ * @see #removeTrigger(Connection, SchedulingContext, String, String)
+ * @see #replaceTrigger(Connection, SchedulingContext, String, String, Trigger)
+ */
+ private boolean deleteTriggerAndChildren(
+ Connection conn, String triggerName, String triggerGroupName)
+ throws SQLException, NoSuchDelegateException {
+ DriverDelegate delegate = getDelegate();
+
+ // Once it succeeds in deleting one sub-table entry it will not try the others.
+ if ((delegate.deleteSimpleTrigger(conn, triggerName, triggerGroupName) == 0) &&
+ (delegate.deleteCronTrigger(conn, triggerName, triggerGroupName) == 0)) {
+ delegate.deleteBlobTrigger(conn, triggerName, triggerGroupName);
+ }
+
+ delegate.deleteTriggerListeners(conn, triggerName, triggerGroupName);
+
+ return (delegate.deleteTrigger(conn, triggerName, triggerGroupName) > 0);
+ }
+
+ /**
+ *
+ * Retrieve the {@link com.fr.third.org.quartz.JobDetail}
for the given
+ * {@link com.fr.third.org.quartz.Job}
.
+ *
Job
to be retrieved.
+ * @param groupName
+ * The group name of the Job
to be retrieved.
+ * @return The desired Job
, or null if there is no match.
+ */
+ public JobDetail retrieveJob(final SchedulingContext ctxt, final String jobName,
+ final String groupName) throws JobPersistenceException {
+ return (JobDetail)executeWithoutLock( // no locks necessary for read...
+ new TransactionCallback() {
+ public Object execute(Connection conn) throws JobPersistenceException {
+ return retrieveJob(conn, ctxt, jobName, groupName);
+ }
+ });
+ }
+
+ protected JobDetail retrieveJob(Connection conn, SchedulingContext ctxt,
+ String jobName, String groupName) throws JobPersistenceException {
+ try {
+ JobDetail job = getDelegate().selectJobDetail(conn, jobName,
+ groupName, getClassLoadHelper());
+ if (job != null) {
+ String[] listeners = getDelegate().selectJobListeners(conn,
+ jobName, groupName);
+ for (int i = 0; i < listeners.length; ++i) {
+ job.addJobListener(listeners[i]);
+ }
+ }
+
+ return job;
+ } catch (ClassNotFoundException e) {
+ throw new JobPersistenceException(
+ "Couldn't retrieve job because a required class was not found: "
+ + e.getMessage(), e,
+ SchedulerException.ERR_PERSISTENCE_JOB_DOES_NOT_EXIST);
+ } catch (IOException e) {
+ throw new JobPersistenceException(
+ "Couldn't retrieve job because the BLOB couldn't be deserialized: "
+ + e.getMessage(), e,
+ SchedulerException.ERR_PERSISTENCE_JOB_DOES_NOT_EXIST);
+ } catch (SQLException e) {
+ throw new JobPersistenceException("Couldn't retrieve job: "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Trigger}
with the
+ * given name.
+ *
+ * If removal of the Trigger
results in an empty group, the
+ * group should be removed from the JobStore
's list of
+ * known group names.
+ *
+ * If removal of the Trigger
results in an 'orphaned' Job
+ * that is not 'durable', then the Job
should be deleted
+ * also.
+ *
Trigger
to be removed.
+ * @param groupName
+ * The group name of the Trigger
to be removed.
+ * @return true
if a Trigger
with the given
+ * name & group was found and removed from the store.
+ */
+ public boolean removeTrigger(final SchedulingContext ctxt, final String triggerName,
+ final String groupName) throws JobPersistenceException {
+ return ((Boolean)executeInLock(
+ LOCK_TRIGGER_ACCESS,
+ new TransactionCallback() {
+ public Object execute(Connection conn) throws JobPersistenceException {
+ return removeTrigger(conn, ctxt, triggerName, groupName) ?
+ Boolean.TRUE : Boolean.FALSE;
+ }
+ })).booleanValue();
+ }
+
+ protected boolean removeTrigger(Connection conn, SchedulingContext ctxt,
+ String triggerName, String groupName)
+ throws JobPersistenceException {
+ boolean removedTrigger = false;
+ try {
+ // this must be called before we delete the trigger, obviously
+ JobDetail job = getDelegate().selectJobForTrigger(conn,
+ triggerName, groupName, getClassLoadHelper());
+
+ removedTrigger =
+ deleteTriggerAndChildren(conn, triggerName, groupName);
+
+ if (null != job && !job.isDurable()) {
+ int numTriggers = getDelegate().selectNumTriggersForJob(conn,
+ job.getName(), job.getGroup());
+ if (numTriggers == 0) {
+ // Don't call removeJob() because we don't want to check for
+ // triggers again.
+ deleteJobAndChildren(conn, ctxt, job.getName(), job.getGroup());
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ throw new JobPersistenceException("Couldn't remove trigger: "
+ + e.getMessage(), e);
+ } catch (SQLException e) {
+ throw new JobPersistenceException("Couldn't remove trigger: "
+ + e.getMessage(), e);
+ }
+
+ return removedTrigger;
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.spi.JobStore#replaceTrigger(com.fr.third.org.quartz.core.SchedulingContext, java.lang.String, java.lang.String, com.fr.third.org.quartz.Trigger)
+ */
+ public boolean replaceTrigger(final SchedulingContext ctxt, final String triggerName,
+ final String groupName, final Trigger newTrigger) throws JobPersistenceException {
+ return ((Boolean)executeInLock(
+ LOCK_TRIGGER_ACCESS,
+ new TransactionCallback() {
+ public Object execute(Connection conn) throws JobPersistenceException {
+ return replaceTrigger(conn, ctxt, triggerName, groupName, newTrigger) ?
+ Boolean.TRUE : Boolean.FALSE;
+ }
+ })).booleanValue();
+ }
+
+ protected boolean replaceTrigger(Connection conn, SchedulingContext ctxt,
+ String triggerName, String groupName, Trigger newTrigger)
+ throws JobPersistenceException {
+ try {
+ // this must be called before we delete the trigger, obviously
+ JobDetail job = getDelegate().selectJobForTrigger(conn,
+ triggerName, groupName, getClassLoadHelper());
+
+ if (job == null) {
+ return false;
+ }
+
+ if (!newTrigger.getJobName().equals(job.getName()) ||
+ !newTrigger.getJobGroup().equals(job.getGroup())) {
+ throw new JobPersistenceException("New trigger is not related to the same job as the old trigger.");
+ }
+
+ boolean removedTrigger =
+ deleteTriggerAndChildren(conn, triggerName, groupName);
+
+ storeTrigger(conn, ctxt, newTrigger, job, false, STATE_WAITING, false, false);
+
+ return removedTrigger;
+ } catch (ClassNotFoundException e) {
+ throw new JobPersistenceException("Couldn't remove trigger: "
+ + e.getMessage(), e);
+ } catch (SQLException e) {
+ throw new JobPersistenceException("Couldn't remove trigger: "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ *
+ * Retrieve the given {@link com.fr.third.org.quartz.Trigger}
.
+ *
Trigger
to be retrieved.
+ * @param groupName
+ * The group name of the Trigger
to be retrieved.
+ * @return The desired Trigger
, or null if there is no
+ * match.
+ */
+ public Trigger retrieveTrigger(final SchedulingContext ctxt, final String triggerName,
+ final String groupName) throws JobPersistenceException {
+ return (Trigger)executeWithoutLock( // no locks necessary for read...
+ new TransactionCallback() {
+ public Object execute(Connection conn) throws JobPersistenceException {
+ return retrieveTrigger(conn, ctxt, triggerName, groupName);
+ }
+ });
+ }
+
+ protected Trigger retrieveTrigger(Connection conn, SchedulingContext ctxt,
+ String triggerName, String groupName)
+ throws JobPersistenceException {
+ return retrieveTrigger(conn, triggerName, groupName);
+ }
+
+ protected Trigger retrieveTrigger(Connection conn, String triggerName, String groupName)
+ throws JobPersistenceException {
+ try {
+ Trigger trigger = getDelegate().selectTrigger(conn, triggerName,
+ groupName);
+ if (trigger == null) {
+ return null;
+ }
+
+ // In case Trigger was BLOB, clear out any listeners that might
+ // have been serialized.
+ trigger.clearAllTriggerListeners();
+
+ String[] listeners = getDelegate().selectTriggerListeners(conn,
+ triggerName, groupName);
+ for (int i = 0; i < listeners.length; ++i) {
+ trigger.addTriggerListener(listeners[i]);
+ }
+
+ return trigger;
+ } catch (Exception e) {
+ throw new JobPersistenceException("Couldn't retrieve trigger: "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ *
+ * Get the current state of the identified {@link Trigger}
.
+ *
+ * Store the given {@link com.fr.third.org.quartz.Calendar}
.
+ *
Calendar
to be stored.
+ * @param replaceExisting
+ * If true
, any Calendar
existing
+ * in the JobStore
with the same name & group
+ * should be over-written.
+ * @throws ObjectAlreadyExistsException
+ * if a Calendar
with the same name already
+ * exists, and replaceExisting is set to false.
+ */
+ public void storeCalendar(final SchedulingContext ctxt, final String calName,
+ final Calendar calendar, final boolean replaceExisting, final boolean updateTriggers)
+ throws ObjectAlreadyExistsException, JobPersistenceException {
+ executeInLock(
+ (isLockOnInsert() || updateTriggers) ? LOCK_TRIGGER_ACCESS : null,
+ new VoidTransactionCallback() {
+ public void execute(Connection conn) throws JobPersistenceException {
+ storeCalendar(conn, ctxt, calName, calendar, replaceExisting, updateTriggers);
+ }
+ });
+ }
+
+ protected void storeCalendar(Connection conn, SchedulingContext ctxt,
+ String calName, Calendar calendar, boolean replaceExisting, boolean updateTriggers)
+ throws ObjectAlreadyExistsException, JobPersistenceException {
+ try {
+ boolean existingCal = calendarExists(conn, calName);
+ if (existingCal && !replaceExisting) {
+ throw new ObjectAlreadyExistsException(
+ "Calendar with name '" + calName + "' already exists.");
+ }
+
+ if (existingCal) {
+ if (getDelegate().updateCalendar(conn, calName, calendar) < 1) {
+ throw new JobPersistenceException(
+ "Couldn't store calendar. Update failed.");
+ }
+
+ if(updateTriggers) {
+ Trigger[] trigs = getDelegate().selectTriggersForCalendar(conn, calName);
+
+ for(int i=0; i < trigs.length; i++) {
+ trigs[i].updateWithNewCalendar(calendar, getMisfireThreshold());
+ storeTrigger(conn, ctxt, trigs[i], null, true, STATE_WAITING, false, false);
+ }
+ }
+ } else {
+ if (getDelegate().insertCalendar(conn, calName, calendar) < 1) {
+ throw new JobPersistenceException(
+ "Couldn't store calendar. Insert failed.");
+ }
+ }
+
+ if (isClustered == false) {
+ calendarCache.put(calName, calendar); // lazy-cache
+ }
+
+ } catch (IOException e) {
+ throw new JobPersistenceException(
+ "Couldn't store calendar because the BLOB couldn't be serialized: "
+ + e.getMessage(), e);
+ } catch (ClassNotFoundException e) {
+ throw new JobPersistenceException("Couldn't store calendar: "
+ + e.getMessage(), e);
+ }catch (SQLException e) {
+ throw new JobPersistenceException("Couldn't store calendar: "
+ + e.getMessage(), e);
+ }
+ }
+
+ protected boolean calendarExists(Connection conn, String calName)
+ throws JobPersistenceException {
+ try {
+ return getDelegate().calendarExists(conn, calName);
+ } catch (SQLException e) {
+ throw new JobPersistenceException(
+ "Couldn't determine calendar existence (" + calName + "): "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Calendar}
with the
+ * given name.
+ *
+ * If removal of the Calendar
would result in
+ * JobPersistenceException
will be thrown.
Calendar
to be removed.
+ * @return true
if a Calendar
with the given name
+ * was found and removed from the store.
+ */
+ public boolean removeCalendar(final SchedulingContext ctxt, final String calName)
+ throws JobPersistenceException {
+ return ((Boolean)executeInLock(
+ LOCK_TRIGGER_ACCESS,
+ new TransactionCallback() {
+ public Object execute(Connection conn) throws JobPersistenceException {
+ return removeCalendar(conn, ctxt, calName) ?
+ Boolean.TRUE : Boolean.FALSE;
+ }
+ })).booleanValue();
+ }
+
+ protected boolean removeCalendar(Connection conn, SchedulingContext ctxt,
+ String calName) throws JobPersistenceException {
+ try {
+ if (getDelegate().calendarIsReferenced(conn, calName)) {
+ throw new JobPersistenceException(
+ "Calender cannot be removed if it referenced by a trigger!");
+ }
+
+ if (isClustered == false) {
+ calendarCache.remove(calName);
+ }
+
+ return (getDelegate().deleteCalendar(conn, calName) > 0);
+ } catch (SQLException e) {
+ throw new JobPersistenceException("Couldn't remove calendar: "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ *
+ * Retrieve the given {@link com.fr.third.org.quartz.Trigger}
.
+ *
Calendar
to be retrieved.
+ * @return The desired Calendar
, or null if there is no
+ * match.
+ */
+ public Calendar retrieveCalendar(final SchedulingContext ctxt, final String calName)
+ throws JobPersistenceException {
+ return (Calendar)executeWithoutLock( // no locks necessary for read...
+ new TransactionCallback() {
+ public Object execute(Connection conn) throws JobPersistenceException {
+ return retrieveCalendar(conn, ctxt, calName);
+ }
+ });
+ }
+
+ protected Calendar retrieveCalendar(Connection conn,
+ SchedulingContext ctxt, String calName)
+ throws JobPersistenceException {
+ // all calendars are persistent, but we can lazy-cache them during run
+ // time as long as we aren't running clustered.
+ Calendar cal = (isClustered) ? null : (Calendar) calendarCache.get(calName);
+ if (cal != null) {
+ return cal;
+ }
+
+ try {
+ cal = getDelegate().selectCalendar(conn, calName);
+ if (isClustered == false) {
+ calendarCache.put(calName, cal); // lazy-cache...
+ }
+ return cal;
+ } catch (ClassNotFoundException e) {
+ throw new JobPersistenceException(
+ "Couldn't retrieve calendar because a required class was not found: "
+ + e.getMessage(), e);
+ } catch (IOException e) {
+ throw new JobPersistenceException(
+ "Couldn't retrieve calendar because the BLOB couldn't be deserialized: "
+ + e.getMessage(), e);
+ } catch (SQLException e) {
+ throw new JobPersistenceException("Couldn't retrieve calendar: "
+ + e.getMessage(), e);
+ }
+ }
+
+ /**
+ *
+ * Get the number of {@link com.fr.third.org.quartz.Job}
s that are
+ * stored in the JobStore
.
+ *
+ * Get the number of {@link com.fr.third.org.quartz.Trigger}
s that are
+ * stored in the JobsStore
.
+ *
+ * Get the number of {@link com.fr.third.org.quartz.Calendar}
s that are
+ * stored in the JobsStore
.
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Job}
s that
+ * have the given group name.
+ *
+ * If there are no jobs in the given group name, the result should be a
+ * zero-length array (not null
).
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Trigger}
s
+ * that have the given group name.
+ *
+ * If there are no triggers in the given group name, the result should be a
+ * zero-length array (not null
).
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Job}
+ * groups.
+ *
+ * If there are no known group names, the result should be a zero-length
+ * array (not null
).
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Trigger}
+ * groups.
+ *
+ * If there are no known group names, the result should be a zero-length
+ * array (not null
).
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Calendar}
s
+ * in the JobStore
.
+ *
+ * If there are no Calendars in the given group name, the result should be
+ * a zero-length array (not null
).
+ *
+ * Get all of the Triggers that are associated to the given Job. + *
+ * + *+ * If there are no matches, a zero-length array should be returned. + *
+ */ + public Trigger[] getTriggersForJob(final SchedulingContext ctxt, final String jobName, + final String groupName) throws JobPersistenceException { + return (Trigger[])executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getTriggersForJob(conn, ctxt, jobName, groupName); + } + }); + } + + protected Trigger[] getTriggersForJob(Connection conn, + SchedulingContext ctxt, String jobName, String groupName) + throws JobPersistenceException { + Trigger[] array = null; + + try { + array = getDelegate() + .selectTriggersForJob(conn, jobName, groupName); + } catch (Exception e) { + throw new JobPersistenceException( + "Couldn't obtain triggers for job: " + e.getMessage(), e); + } + + return array; + } + + /** + *
+ * Pause the {@link com.fr.third.org.quartz.Trigger}
with the given name.
+ *
+ * Pause the {@link com.fr.third.org.quartz.Trigger}
with the given name.
+ *
+ * Pause the {@link com.fr.third.org.quartz.Job}
with the given name - by
+ * pausing all of its current Trigger
s.
+ *
+ * Pause all of the {@link com.fr.third.org.quartz.Job}s
in the given
+ * group - by pausing all of their Trigger
s.
+ *
+ * Resume (un-pause) the {@link com.fr.third.org.quartz.Trigger}
with the
+ * given name.
+ *
+ * If the Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) the {@link com.fr.third.org.quartz.Trigger}
with the
+ * given name.
+ *
+ * If the Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) the {@link com.fr.third.org.quartz.Job}
with the
+ * given name.
+ *
+ * If any of the Job
'sTrigger
s missed one
+ * or more fire-times, then the Trigger
's misfire
+ * instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link com.fr.third.org.quartz.Job}s
in
+ * the given group.
+ *
+ * If any of the Job
s had Trigger
s that
+ * missed one or more fire-times, then the Trigger
's
+ * misfire instruction will be applied.
+ *
+ * Pause all of the {@link com.fr.third.org.quartz.Trigger}s
in the
+ * given group.
+ *
+ * Pause all of the {@link com.fr.third.org.quartz.Trigger}s
in the
+ * given group.
+ *
+ * Pause all of the {@link com.fr.third.org.quartz.Trigger}s
in the
+ * given group.
+ *
+ * Resume (un-pause) all of the {@link com.fr.third.org.quartz.Trigger}s
+ * in the given group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link com.fr.third.org.quartz.Trigger}s
+ * in the given group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Pause all triggers - equivalent of calling pauseTriggerGroup(group)
+ * on every group.
+ *
+ * When resumeAll()
is called (to un-pause), trigger misfire
+ * instructions WILL be applied.
+ *
+ * Pause all triggers - equivalent of calling pauseTriggerGroup(group)
+ * on every group.
+ *
+ * When resumeAll()
is called (to un-pause), trigger misfire
+ * instructions WILL be applied.
+ *
+ * Resume (un-pause) all triggers - equivalent of calling resumeTriggerGroup(group)
+ * on every group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) all triggers - equivalent of calling resumeTriggerGroup(group)
+ * on every group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Get a handle to the next N triggers to be fired, and mark them as 'reserved' + * by the calling scheduler. + *
+ * + * @see #releaseAcquiredTrigger(SchedulingContext, Trigger) + */ + public Trigger acquireNextTrigger(final SchedulingContext ctxt, final long noLaterThan) + throws JobPersistenceException { + + if(isAcquireTriggersWithinLock()) { // behavior before Quartz 1.6.3 release + return (Trigger)executeInNonManagedTXLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return acquireNextTrigger(conn, ctxt, noLaterThan); + } + }); + } + else { // default behavior since Quartz 1.6.3 release + return (Trigger)executeWithoutLock( + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return acquireNextTrigger(conn, ctxt, noLaterThan); + } + }); + } + } + + // TODO: this really ought to return something like a FiredTriggerBundle, + // so that the fireInstanceId doesn't have to be on the trigger... + protected Trigger acquireNextTrigger(Connection conn, SchedulingContext ctxt, long noLaterThan) + throws JobPersistenceException { + do { + try { + Trigger nextTrigger = null; + + List keys = getDelegate().selectTriggerToAcquire(conn, noLaterThan, getMisfireTime()); + + // No trigger is ready to fire yet. + if (keys == null || keys.size() == 0) + return null; + + Iterator itr = keys.iterator(); + while(itr.hasNext()) { + Key triggerKey = (Key) itr.next(); + + int rowsUpdated = + getDelegate().updateTriggerStateFromOtherState( + conn, + triggerKey.getName(), triggerKey.getGroup(), + STATE_ACQUIRED, STATE_WAITING); + + // If our trigger was no longer in the expected state, try a new one. + if (rowsUpdated <= 0) { + continue; + } + + nextTrigger = + retrieveTrigger(conn, ctxt, triggerKey.getName(), triggerKey.getGroup()); + + // If our trigger is no longer available, try a new one. + if(nextTrigger == null) { + continue; + } + + break; + } + + // if we didn't end up with a trigger to fire from that first + // batch, try again for another batch + if(nextTrigger == null) { + continue; + } + + nextTrigger.setFireInstanceId(getFiredTriggerRecordId()); + getDelegate().insertFiredTrigger(conn, nextTrigger, STATE_ACQUIRED, null); + + return nextTrigger; + } catch (Exception e) { + throw new JobPersistenceException( + "Couldn't acquire next trigger: " + e.getMessage(), e); + } + } while (true); + } + + /** + *
+ * Inform the JobStore
that the scheduler no longer plans to
+ * fire the given Trigger
, that it had previously acquired
+ * (reserved).
+ *
+ * Inform the JobStore
that the scheduler is now firing the
+ * given Trigger
(executing its associated Job
),
+ * that it had previously acquired (reserved).
+ *
+ * Inform the JobStore
that the scheduler has completed the
+ * firing of the given Trigger
(and the execution its
+ * associated Job
), and that the {@link com.fr.third.org.quartz.JobDataMap}
+ * in the given JobDetail
should be updated if the Job
+ * is stateful.
+ *
+ * Get the driver delegate for DB operations. + *
+ */ + protected DriverDelegate getDelegate() throws NoSuchDelegateException { + if (null == delegate) { + try { + if(delegateClassName != null) { + delegateClass = + getClassLoadHelper().loadClass(delegateClassName); + } + + Constructor ctor = null; + Object[] ctorParams = null; + if (canUseProperties()) { + Class[] ctorParamTypes = new Class[]{ + Log.class, String.class, String.class, Boolean.class}; + ctor = delegateClass.getConstructor(ctorParamTypes); + ctorParams = new Object[]{ + getLog(), tablePrefix, + instanceId, new Boolean(canUseProperties())}; + } else { + Class[] ctorParamTypes = new Class[]{ + Log.class, String.class, String.class}; + ctor = delegateClass.getConstructor(ctorParamTypes); + ctorParams = new Object[]{getLog(), tablePrefix, instanceId}; + } + + delegate = (DriverDelegate) ctor.newInstance(ctorParams); + } catch (NoSuchMethodException e) { + throw new NoSuchDelegateException( + "Couldn't find delegate constructor: " + e.getMessage()); + } catch (InstantiationException e) { + throw new NoSuchDelegateException("Couldn't create delegate: " + + e.getMessage()); + } catch (IllegalAccessException e) { + throw new NoSuchDelegateException("Couldn't create delegate: " + + e.getMessage()); + } catch (InvocationTargetException e) { + throw new NoSuchDelegateException("Couldn't create delegate: " + + e.getMessage()); + } catch (ClassNotFoundException e) { + throw new NoSuchDelegateException("Couldn't load delegate class: " + + e.getMessage()); + } + } + + return delegate; + } + + protected Semaphore getLockHandler() { + return lockHandler; + } + + public void setLockHandler(Semaphore lockHandler) { + this.lockHandler = lockHandler; + } + + //--------------------------------------------------------------------------- + // Management methods + //--------------------------------------------------------------------------- + + protected RecoverMisfiredJobsResult doRecoverMisfires() throws JobPersistenceException { + boolean transOwner = false; + Connection conn = getNonManagedTXConnection(); + try { + RecoverMisfiredJobsResult result = RecoverMisfiredJobsResult.NO_OP; + + // Before we make the potentially expensive call to acquire the + // trigger lock, peek ahead to see if it is likely we would find + // misfired triggers requiring recovery. + int misfireCount = (getDoubleCheckLockMisfireHandler()) ? + getDelegate().countMisfiredTriggersInStates( + conn, STATE_MISFIRED, STATE_WAITING, getMisfireTime()) : + Integer.MAX_VALUE; + + if (misfireCount == 0) { + getLog().debug( + "Found 0 triggers that missed their scheduled fire-time."); + } else { + transOwner = getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); + + result = recoverMisfiredJobs(conn, false); + } + + commitConnection(conn); + return result; + } catch (JobPersistenceException e) { + rollbackConnection(conn); + throw e; + } catch (SQLException e) { + rollbackConnection(conn); + throw new JobPersistenceException("Database error recovering from misfires.", e); + } catch (RuntimeException e) { + rollbackConnection(conn); + throw new JobPersistenceException("Unexpected runtime exception: " + + e.getMessage(), e); + } finally { + try { + releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); + } finally { + cleanupConnection(conn); + } + } + } + + protected void signalSchedulingChange(long candidateNewNextFireTime) { + signaler.signalSchedulingChange(candidateNewNextFireTime); + } + + //--------------------------------------------------------------------------- + // Cluster management methods + //--------------------------------------------------------------------------- + + protected boolean firstCheckIn = true; + + protected long lastCheckin = System.currentTimeMillis(); + + protected boolean doCheckin() throws JobPersistenceException { + boolean transOwner = false; + boolean transStateOwner = false; + boolean recovered = false; + + Connection conn = getNonManagedTXConnection(); + try { + // Other than the first time, always checkin first to make sure there is + // work to be done before we aquire the lock (since that is expensive, + // and is almost never necessary). This must be done in a separate + // transaction to prevent a deadlock under recovery conditions. + List failedRecords = null; + if (firstCheckIn == false) { + boolean succeeded = false; + try { + failedRecords = clusterCheckIn(conn); + commitConnection(conn); + succeeded = true; + } catch (JobPersistenceException e) { + rollbackConnection(conn); + throw e; + } finally { + // Only cleanup the connection if we failed and are bailing + // as we will otherwise continue to use it. + if (succeeded == false) { + cleanupConnection(conn); + } + } + } + + if (firstCheckIn || (failedRecords.size() > 0)) { + getLockHandler().obtainLock(conn, LOCK_STATE_ACCESS); + transStateOwner = true; + + // Now that we own the lock, make sure we still have work to do. + // The first time through, we also need to make sure we update/create our state record + failedRecords = (firstCheckIn) ? clusterCheckIn(conn) : findFailedInstances(conn); + + if (failedRecords.size() > 0) { + getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); + //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); + transOwner = true; + + clusterRecover(conn, failedRecords); + recovered = true; + } + } + + commitConnection(conn); + } catch (JobPersistenceException e) { + rollbackConnection(conn); + throw e; + } finally { + try { + releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); + } finally { + try { + releaseLock(conn, LOCK_STATE_ACCESS, transStateOwner); + } finally { + cleanupConnection(conn); + } + } + } + + firstCheckIn = false; + + return recovered; + } + + /** + * Get a list of all scheduler instances in the cluster that may have failed. + * This includes this scheduler if it is checking in for the first time. + */ + protected List findFailedInstances(Connection conn) + throws JobPersistenceException { + try { + List failedInstances = new LinkedList(); + boolean foundThisScheduler = false; + long timeNow = System.currentTimeMillis(); + + List states = getDelegate().selectSchedulerStateRecords(conn, null); + + for (Iterator itr = states.iterator(); itr.hasNext();) { + SchedulerStateRecord rec = (SchedulerStateRecord) itr.next(); + + // find own record... + if (rec.getSchedulerInstanceId().equals(getInstanceId())) { + foundThisScheduler = true; + if (firstCheckIn) { + failedInstances.add(rec); + } + } else { + // find failed instances... + if (calcFailedIfAfter(rec) < timeNow) { + failedInstances.add(rec); + } + } + } + + // The first time through, also check for orphaned fired triggers. + if (firstCheckIn) { + failedInstances.addAll(findOrphanedFailedInstances(conn, states)); + } + + // If not the first time but we didn't find our own instance, then + // Someone must have done recovery for us. + if ((foundThisScheduler == false) && (firstCheckIn == false)) { + // TODO: revisit when handle self-failed-out implied (see TODO in clusterCheckIn() below) + getLog().warn( + "This scheduler instance (" + getInstanceId() + ") is still " + + "active but was recovered by another instance in the cluster. " + + "This may cause inconsistent behavior."); + } + + return failedInstances; + } catch (Exception e) { + lastCheckin = System.currentTimeMillis(); + throw new JobPersistenceException("Failure identifying failed instances when checking-in: " + + e.getMessage(), e); + } + } + + /** + * Create dummySchedulerStateRecord
objects for fired triggers
+ * that have no scheduler state record. Checkin timestamp and interval are
+ * left as zero on these dummy SchedulerStateRecord
objects.
+ *
+ * @param schedulerStateRecords List of all current SchedulerStateRecords
+ */
+ private List findOrphanedFailedInstances(
+ Connection conn,
+ List schedulerStateRecords)
+ throws SQLException, NoSuchDelegateException {
+ List orphanedInstances = new ArrayList();
+
+ Set allFiredTriggerInstanceNames = getDelegate().selectFiredTriggerInstanceNames(conn);
+ if (allFiredTriggerInstanceNames.isEmpty() == false) {
+ for (Iterator schedulerStateIter = schedulerStateRecords.iterator();
+ schedulerStateIter.hasNext();) {
+ SchedulerStateRecord rec = (SchedulerStateRecord)schedulerStateIter.next();
+
+ allFiredTriggerInstanceNames.remove(rec.getSchedulerInstanceId());
+ }
+
+ for (Iterator orphanIter = allFiredTriggerInstanceNames.iterator();
+ orphanIter.hasNext();) {
+
+ SchedulerStateRecord orphanedInstance = new SchedulerStateRecord();
+ orphanedInstance.setSchedulerInstanceId((String)orphanIter.next());
+
+ orphanedInstances.add(orphanedInstance);
+
+ getLog().warn(
+ "Found orphaned fired triggers for instance: " + orphanedInstance.getSchedulerInstanceId());
+ }
+ }
+
+ return orphanedInstances;
+ }
+
+ protected long calcFailedIfAfter(SchedulerStateRecord rec) {
+ return rec.getCheckinTimestamp() +
+ Math.max(rec.getCheckinInterval(),
+ (System.currentTimeMillis() - lastCheckin)) +
+ 7500L;
+ }
+
+ protected List clusterCheckIn(Connection conn)
+ throws JobPersistenceException {
+
+ List failedInstances = findFailedInstances(conn);
+
+ try {
+ // TODO: handle self-failed-out
+
+ // check in...
+ lastCheckin = System.currentTimeMillis();
+ if(getDelegate().updateSchedulerState(conn, getInstanceId(), lastCheckin) == 0) {
+ getDelegate().insertSchedulerState(conn, getInstanceId(),
+ lastCheckin, getClusterCheckinInterval());
+ }
+
+ } catch (Exception e) {
+ throw new JobPersistenceException("Failure updating scheduler state when checking-in: "
+ + e.getMessage(), e);
+ }
+
+ return failedInstances;
+ }
+
+ protected void clusterRecover(Connection conn, List failedInstances)
+ throws JobPersistenceException {
+
+ if (failedInstances.size() > 0) {
+
+ long recoverIds = System.currentTimeMillis();
+
+ logWarnIfNonZero(failedInstances.size(),
+ "ClusterManager: detected " + failedInstances.size()
+ + " failed or restarted instances.");
+ try {
+ Iterator itr = failedInstances.iterator();
+ while (itr.hasNext()) {
+ SchedulerStateRecord rec = (SchedulerStateRecord) itr
+ .next();
+
+ getLog().info(
+ "ClusterManager: Scanning for instance \""
+ + rec.getSchedulerInstanceId()
+ + "\"'s failed in-progress jobs.");
+
+ List firedTriggerRecs = getDelegate()
+ .selectInstancesFiredTriggerRecords(conn,
+ rec.getSchedulerInstanceId());
+
+ int acquiredCount = 0;
+ int recoveredCount = 0;
+ int otherCount = 0;
+
+ Set triggerKeys = new HashSet();
+
+ Iterator ftItr = firedTriggerRecs.iterator();
+ while (ftItr.hasNext()) {
+ FiredTriggerRecord ftRec = (FiredTriggerRecord) ftItr
+ .next();
+
+ Key tKey = ftRec.getTriggerKey();
+ Key jKey = ftRec.getJobKey();
+
+ triggerKeys.add(tKey);
+
+ // release blocked triggers..
+ if (ftRec.getFireInstanceState().equals(STATE_BLOCKED)) {
+ getDelegate()
+ .updateTriggerStatesForJobFromOtherState(
+ conn, jKey.getName(),
+ jKey.getGroup(), STATE_WAITING,
+ STATE_BLOCKED);
+ } else if (ftRec.getFireInstanceState().equals(STATE_PAUSED_BLOCKED)) {
+ getDelegate()
+ .updateTriggerStatesForJobFromOtherState(
+ conn, jKey.getName(),
+ jKey.getGroup(), STATE_PAUSED,
+ STATE_PAUSED_BLOCKED);
+ }
+
+ // release acquired triggers..
+ if (ftRec.getFireInstanceState().equals(STATE_ACQUIRED)) {
+ getDelegate().updateTriggerStateFromOtherState(
+ conn, tKey.getName(), tKey.getGroup(),
+ STATE_WAITING, STATE_ACQUIRED);
+ acquiredCount++;
+ } else if (ftRec.isJobRequestsRecovery()) {
+ // handle jobs marked for recovery that were not fully
+ // executed..
+ if (jobExists(conn, jKey.getName(), jKey.getGroup())) {
+ SimpleTrigger rcvryTrig = new SimpleTrigger(
+ "recover_"
+ + rec.getSchedulerInstanceId()
+ + "_"
+ + String.valueOf(recoverIds++),
+ Scheduler.DEFAULT_RECOVERY_GROUP,
+ new Date(ftRec.getFireTimestamp()));
+ rcvryTrig.setVolatility(ftRec.isTriggerIsVolatile());
+ rcvryTrig.setJobName(jKey.getName());
+ rcvryTrig.setJobGroup(jKey.getGroup());
+ rcvryTrig.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
+ rcvryTrig.setPriority(ftRec.getPriority());
+ JobDataMap jd = getDelegate().selectTriggerJobDataMap(conn, tKey.getName(), tKey.getGroup());
+ jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_NAME, tKey.getName());
+ jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_GROUP, tKey.getGroup());
+ jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_FIRETIME_IN_MILLISECONDS, String.valueOf(ftRec.getFireTimestamp()));
+ rcvryTrig.setJobDataMap(jd);
+
+ rcvryTrig.computeFirstFireTime(null);
+ storeTrigger(conn, null, rcvryTrig, null, false,
+ STATE_WAITING, false, true);
+ recoveredCount++;
+ } else {
+ getLog()
+ .warn(
+ "ClusterManager: failed job '"
+ + jKey
+ + "' no longer exists, cannot schedule recovery.");
+ otherCount++;
+ }
+ } else {
+ otherCount++;
+ }
+
+ // free up stateful job's triggers
+ if (ftRec.isJobIsStateful()) {
+ getDelegate()
+ .updateTriggerStatesForJobFromOtherState(
+ conn, jKey.getName(),
+ jKey.getGroup(), STATE_WAITING,
+ STATE_BLOCKED);
+ getDelegate()
+ .updateTriggerStatesForJobFromOtherState(
+ conn, jKey.getName(),
+ jKey.getGroup(), STATE_PAUSED,
+ STATE_PAUSED_BLOCKED);
+ }
+ }
+
+ getDelegate().deleteFiredTriggers(conn,
+ rec.getSchedulerInstanceId());
+
+ // Check if any of the fired triggers we just deleted were the last fired trigger
+ // records of a COMPLETE trigger.
+ int completeCount = 0;
+ for (Iterator triggerKeyIter = triggerKeys.iterator(); triggerKeyIter.hasNext();) {
+ Key triggerKey = (Key)triggerKeyIter.next();
+
+ if (getDelegate().selectTriggerState(conn, triggerKey.getName(), triggerKey.getGroup()).
+ equals(STATE_COMPLETE)) {
+ List firedTriggers =
+ getDelegate().selectFiredTriggerRecords(conn, triggerKey.getName(), triggerKey.getGroup());
+ if (firedTriggers.isEmpty()) {
+ SchedulingContext schedulingContext = new SchedulingContext();
+ schedulingContext.setInstanceId(instanceId);
+
+ if (removeTrigger(conn, schedulingContext, triggerKey.getName(), triggerKey.getGroup())) {
+ completeCount++;
+ }
+ }
+ }
+ }
+
+ logWarnIfNonZero(acquiredCount,
+ "ClusterManager: ......Freed " + acquiredCount
+ + " acquired trigger(s).");
+ logWarnIfNonZero(completeCount,
+ "ClusterManager: ......Deleted " + completeCount
+ + " complete triggers(s).");
+ logWarnIfNonZero(recoveredCount,
+ "ClusterManager: ......Scheduled " + recoveredCount
+ + " recoverable job(s) for recovery.");
+ logWarnIfNonZero(otherCount,
+ "ClusterManager: ......Cleaned-up " + otherCount
+ + " other failed job(s).");
+
+ if (rec.getSchedulerInstanceId().equals(getInstanceId()) == false) {
+ getDelegate().deleteSchedulerState(conn,
+ rec.getSchedulerInstanceId());
+ }
+ }
+ } catch (Exception e) {
+ throw new JobPersistenceException("Failure recovering jobs: "
+ + e.getMessage(), e);
+ }
+ }
+ }
+
+ protected void logWarnIfNonZero(int val, String warning) {
+ if (val > 0) {
+ getLog().info(warning);
+ } else {
+ getLog().debug(warning);
+ }
+ }
+
+ /**
+ * + * Cleanup the given database connection. This means restoring + * any modified auto commit or transaction isolation connection + * attributes, and then closing the underlying connection. + *
+ * + *+ * This is separate from closeConnection() because the Spring + * integration relies on being able to overload closeConnection() and + * expects the same connection back that it originally returned + * from the datasource. + *
+ * + * @see #closeConnection(Connection) + */ + protected void cleanupConnection(Connection conn) { + if (conn != null) { + if (conn instanceof Proxy) { + Proxy connProxy = (Proxy)conn; + + InvocationHandler invocationHandler = + Proxy.getInvocationHandler(connProxy); + if (invocationHandler instanceof AttributeRestoringConnectionInvocationHandler) { + AttributeRestoringConnectionInvocationHandler connHandler = + (AttributeRestoringConnectionInvocationHandler)invocationHandler; + + connHandler.restoreOriginalAtributes(); + closeConnection(connHandler.getWrappedConnection()); + return; + } + } + + // Wan't a Proxy, or was a Proxy, but wasn't ours. + closeConnection(conn); + } + } + + + /** + * Closes the suppliedConnection
.
+ *
+ * Ignores a null Connection
.
+ * Any exception thrown trying to close the Connection
is
+ * logged and ignored.
+ *
Connection
to close (Optional).
+ */
+ protected void closeConnection(Connection conn) {
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (SQLException e) {
+ getLog().error("Failed to close Connection", e);
+ } catch (Throwable e) {
+ getLog().error(
+ "Unexpected exception closing Connection." +
+ " This is often due to a Connection being returned after or during shutdown.", e);
+ }
+ }
+ }
+
+ /**
+ * Rollback the supplied connection.
+ *
+ * + * Logs any SQLException it gets trying to rollback, but will not propogate + * the exception lest it mask the exception that caused the caller to + * need to rollback in the first place. + *
+ * + * @param conn (Optional) + */ + protected void rollbackConnection(Connection conn) { + if (conn != null) { + try { + conn.rollback(); + } catch (SQLException e) { + getLog().error( + "Couldn't rollback jdbc connection. "+e.getMessage(), e); + } + } + } + + /** + * Commit the supplied connection + * + * @param conn (Optional) + * @throws JobPersistenceException thrown if a SQLException occurs when the + * connection is committed + */ + protected void commitConnection(Connection conn) + throws JobPersistenceException { + + if (conn != null) { + try { + conn.commit(); + } catch (SQLException e) { + throw new JobPersistenceException( + "Couldn't commit jdbc connection. "+e.getMessage(), e); + } + } + } + + /** + * Implement this interface to provide the code to execute within + * the a transaction template. If no return value is required, execute + * should just return null. + * + * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback) + * @see JobStoreSupport#executeInLock(String, TransactionCallback) + * @see JobStoreSupport#executeWithoutLock(TransactionCallback) + */ + protected interface TransactionCallback { + Object execute(Connection conn) throws JobPersistenceException; + } + + /** + * Implement this interface to provide the code to execute within + * the a transaction template that has no return value. + * + * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback) + */ + protected interface VoidTransactionCallback { + void execute(Connection conn) throws JobPersistenceException; + } + + /** + * Execute the given callback in a transaction. Depending on the JobStore, + * the surrounding transaction may be assumed to be already present + * (managed). + * + *+ * This method just forwards to executeInLock() with a null lockName. + *
+ * + * @see #executeInLock(String, TransactionCallback) + */ + public Object executeWithoutLock( + TransactionCallback txCallback) throws JobPersistenceException { + return executeInLock(null, txCallback); + } + + /** + * Execute the given callback having aquired the given lock. + * Depending on the JobStore, the surrounding transaction may be + * assumed to be already present (managed). This version is just a + * handy wrapper around executeInLock that doesn't require a return + * value. + * + * @param lockName The name of the lock to aquire, for example + * "TRIGGER_ACCESS". If null, then no lock is aquired, but the + * lockCallback is still executed in a transaction. + * + * @see #executeInLock(String, TransactionCallback) + */ + protected void executeInLock( + final String lockName, + final VoidTransactionCallback txCallback) throws JobPersistenceException { + executeInLock( + lockName, + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + txCallback.execute(conn); + return null; + } + }); + } + + /** + * Execute the given callback having aquired the given lock. + * Depending on the JobStore, the surrounding transaction may be + * assumed to be already present (managed). + * + * @param lockName The name of the lock to aquire, for example + * "TRIGGER_ACCESS". If null, then no lock is aquired, but the + * lockCallback is still executed in a transaction. + */ + protected abstract Object executeInLock( + String lockName, + TransactionCallback txCallback) throws JobPersistenceException; + + /** + * Execute the given callback having optionally aquired the given lock. + * This uses the non-managed transaction connection. This version is just a + * handy wrapper around executeInNonManagedTXLock that doesn't require a return + * value. + * + * @param lockName The name of the lock to aquire, for example + * "TRIGGER_ACCESS". If null, then no lock is aquired, but the + * lockCallback is still executed in a non-managed transaction. + * + * @see #executeInNonManagedTXLock(String, TransactionCallback) + */ + protected void executeInNonManagedTXLock( + final String lockName, + final VoidTransactionCallback txCallback) throws JobPersistenceException { + executeInNonManagedTXLock( + lockName, + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + txCallback.execute(conn); + return null; + } + }); + } + + /** + * Execute the given callback having optionally aquired the given lock. + * This uses the non-managed transaction connection. + * + * @param lockName The name of the lock to aquire, for example + * "TRIGGER_ACCESS". If null, then no lock is aquired, but the + * lockCallback is still executed in a non-managed transaction. + */ + protected Object executeInNonManagedTXLock( + String lockName, + TransactionCallback txCallback) throws JobPersistenceException { + boolean transOwner = false; + Connection conn = null; + try { + if (lockName != null) { + // If we aren't using db locks, then delay getting DB connection + // until after aquiring the lock since it isn't needed. + if (getLockHandler().requiresConnection()) { + conn = getNonManagedTXConnection(); + } + + transOwner = getLockHandler().obtainLock(conn, lockName); + } + + if (conn == null) { + conn = getNonManagedTXConnection(); + } + + Object result = txCallback.execute(conn); + commitConnection(conn); + return result; + } catch (JobPersistenceException e) { + rollbackConnection(conn); + throw e; + } catch (RuntimeException e) { + rollbackConnection(conn); + throw new JobPersistenceException("Unexpected runtime exception: " + + e.getMessage(), e); + } finally { + try { + releaseLock(conn, lockName, transOwner); + } finally { + cleanupConnection(conn); + } + } + } + + ///////////////////////////////////////////////////////////////////////////// + // + // ClusterManager Thread + // + ///////////////////////////////////////////////////////////////////////////// + + class ClusterManager extends Thread { + + private boolean shutdown = false; + + private int numFails = 0; + + ClusterManager() { + this.setPriority(Thread.NORM_PRIORITY + 2); + this.setName("QuartzScheduler_" + instanceName + "-" + instanceId + "_ClusterManager"); + this.setDaemon(getMakeThreadsDaemons()); + } + + public void initialize() { + this.manage(); + this.start(); + } + + public void shutdown() { + shutdown = true; + this.interrupt(); + } + + private boolean manage() { + boolean res = false; + try { + + res = doCheckin(); + + numFails = 0; + getLog().debug("ClusterManager: Check-in complete."); + } catch (Exception e) { + if(numFails % 4 == 0) { + getLog().error( + "ClusterManager: Error managing cluster: " + + e.getMessage(), e); + } + numFails++; + } + return res; + } + + public void run() { + while (!shutdown) { + + if (!shutdown) { + long timeToSleep = getClusterCheckinInterval(); + long transpiredTime = (System.currentTimeMillis() - lastCheckin); + timeToSleep = timeToSleep - transpiredTime; + if (timeToSleep <= 0) { + timeToSleep = 100L; + } + + if(numFails > 0) { + timeToSleep = Math.max(getDbRetryInterval(), timeToSleep); + } + + try { + Thread.sleep(timeToSleep); + } catch (Exception ignore) { + } + } + + if (!shutdown && this.manage()) { + signalSchedulingChange(0L); + } + + }//while !shutdown + } + } + + ///////////////////////////////////////////////////////////////////////////// + // + // MisfireHandler Thread + // + ///////////////////////////////////////////////////////////////////////////// + + class MisfireHandler extends Thread { + + private boolean shutdown = false; + + private int numFails = 0; + + + MisfireHandler() { + this.setName("QuartzScheduler_" + instanceName + "-" + instanceId + "_MisfireHandler"); + this.setDaemon(getMakeThreadsDaemons()); + } + + public void initialize() { + //this.manage(); + this.start(); + } + + public void shutdown() { + shutdown = true; + this.interrupt(); + } + + private RecoverMisfiredJobsResult manage() { + try { + getLog().debug("MisfireHandler: scanning for misfires..."); + + RecoverMisfiredJobsResult res = doRecoverMisfires(); + numFails = 0; + return res; + } catch (Exception e) { + if(numFails % 4 == 0) { + getLog().error( + "MisfireHandler: Error handling misfires: " + + e.getMessage(), e); + } + numFails++; + } + return RecoverMisfiredJobsResult.NO_OP; + } + + public void run() { + + while (!shutdown) { + + long sTime = System.currentTimeMillis(); + + RecoverMisfiredJobsResult recoverMisfiredJobsResult = manage(); + + if (recoverMisfiredJobsResult.getProcessedMisfiredTriggerCount() > 0) { + signalSchedulingChange(recoverMisfiredJobsResult.getEarliestNewTime()); + } + + if (!shutdown) { + long timeToSleep = 50l; // At least a short pause to help balance threads + if (!recoverMisfiredJobsResult.hasMoreMisfiredTriggers()) { + timeToSleep = getMisfireThreshold() - (System.currentTimeMillis() - sTime); + if (timeToSleep <= 0) { + timeToSleep = 50l; + } + + if(numFails > 0) { + timeToSleep = Math.max(getDbRetryInterval(), timeToSleep); + } + } + + try { + Thread.sleep(timeToSleep); + } catch (Exception ignore) { + } + }//while !shutdown + } + } + } +} + +// EOF diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/JobStoreTX.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/JobStoreTX.java new file mode 100644 index 000000000..faec53d41 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/JobStoreTX.java @@ -0,0 +1,96 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.impl.jdbcjobstore; + +import java.sql.Connection; + +import com.fr.third.org.quartz.JobPersistenceException; +import com.fr.third.org.quartz.SchedulerConfigException; +import com.fr.third.org.quartz.spi.ClassLoadHelper; +import com.fr.third.org.quartz.spi.SchedulerSignaler; + +/** + *
+ * JobStoreTX
is meant to be used in a standalone environment.
+ * Both commit and rollback will be handled by this class.
+ *
+ * If you need a {@link com.fr.third.org.quartz.spi.JobStore}
class to use
+ * within an application-server environment, use {@link
+ * com.fr.third.org.quartz.impl.jdbcjobstore.JobStoreCMT}
+ * instead.
+ *
JobStoreTX
, the non-managed TX connection is just
+ * the normal connection because it is not CMT.
+ *
+ * @see JobStoreSupport#getConnection()
+ */
+ protected Connection getNonManagedTXConnection()
+ throws JobPersistenceException {
+ return getConnection();
+ }
+
+ /**
+ * Execute the given callback having optionally aquired the given lock.
+ * For JobStoreTX
, because it manages its own transactions
+ * and only has the one datasource, this is the same behavior as
+ * executeInNonManagedTXLock().
+ *
+ * @param lockName The name of the lock to aquire, for example
+ * "TRIGGER_ACCESS". If null, then no lock is aquired, but the
+ * lockCallback is still executed in a transaction.
+ *
+ * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback)
+ * @see JobStoreCMT#executeInLock(String, TransactionCallback)
+ * @see JobStoreSupport#getNonManagedTXConnection()
+ * @see JobStoreSupport#getConnection()
+ */
+ protected Object executeInLock(
+ String lockName,
+ TransactionCallback txCallback) throws JobPersistenceException {
+ return executeInNonManagedTXLock(lockName, txCallback);
+ }
+}
+// EOF
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/LockException.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/LockException.java
new file mode 100644
index 000000000..6c696831e
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/LockException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import com.fr.third.org.quartz.JobPersistenceException;
+
+/**
+ * + * Exception class for when there is a failure obtaining or releasing a + * resource lock. + *
+ * + * @see Semaphore + * + * @author James House + */ +public class LockException extends JobPersistenceException { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public LockException(String msg) { + super(msg); + } + + public LockException(String msg, Throwable cause) { + super(msg, cause); + } +} + +// EOF diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/MSSQLDelegate.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/MSSQLDelegate.java new file mode 100644 index 000000000..fa2be5ee8 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/MSSQLDelegate.java @@ -0,0 +1,108 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.impl.jdbcjobstore; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.apache.commons.logging.Log; + +/** + *+ * This is a driver delegate for the MSSQL JDBC driver. + *
+ * + * @author Jeffrey Wescott + */ +public class MSSQLDelegate extends StdJDBCDelegate { + /** + *+ * Create new MSSQLDelegate instance. + *
+ * + * @param log + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + */ + public MSSQLDelegate(Log log, String tablePrefix, String instanceId) { + super(log, tablePrefix, instanceId); + } + + public MSSQLDelegate(Log log, String tablePrefix, String instanceId, Boolean useProperties) { + super(log, tablePrefix, instanceId, useProperties); + } + + //--------------------------------------------------------------------------- + // protected methods that can be overridden by subclasses + //--------------------------------------------------------------------------- + + /** + *
+ * This method should be overridden by any delegate subclasses that need
+ * special handling for BLOBs. The default implementation uses standard
+ * JDBC java.sql.Blob
operations.
+ *
+ * Exception class for when a driver delegate cannot be found for a given + * configuration, or lack thereof. + *
+ * + * @author Jeffrey Wescott + */ +public class NoSuchDelegateException extends JobPersistenceException { + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public NoSuchDelegateException(String msg) { + super(msg); + } +} + +// EOF diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/PointbaseDelegate.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/PointbaseDelegate.java new file mode 100644 index 000000000..c5d658833 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/PointbaseDelegate.java @@ -0,0 +1,507 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.impl.jdbcjobstore; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.apache.commons.logging.Log; +import com.fr.third.org.quartz.Calendar; +import com.fr.third.org.quartz.CronTrigger; +import com.fr.third.org.quartz.JobDetail; +import com.fr.third.org.quartz.SimpleTrigger; +import com.fr.third.org.quartz.Trigger; + +/** + *+ * This is a driver delegate for the Pointbase JDBC driver. + *
+ * + * @author Gregg Freeman + */ +public class PointbaseDelegate extends StdJDBCDelegate { + + //private static Category log = + // Category.getInstance(PointbaseJDBCDelegate.class); + /** + *+ * Create new PointbaseJDBCDelegate instance. + *
+ * + * @param logger + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + */ + public PointbaseDelegate(Log logger, String tablePrefix, String instanceId) { + super(logger, tablePrefix, instanceId); + } + + /** + *+ * Create new PointbaseJDBCDelegate instance. + *
+ * + * @param logger + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + */ + public PointbaseDelegate(Log logger, String tablePrefix, String instanceId, + Boolean useProperties) { + super(logger, tablePrefix, instanceId, useProperties); + } + + //--------------------------------------------------------------------------- + // jobs + //--------------------------------------------------------------------------- + + /** + *+ * Insert the job detail record. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to insert + * @return number of rows inserted + * @throws IOException + * if there were problems serializing the JobDataMap + */ + public int insertJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException { + //log.debug( "Inserting JobDetail " + job ); + ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); + int len = baos.toByteArray().length; + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + + PreparedStatement ps = null; + + int insertResult = 0; + + try { + ps = conn.prepareStatement(rtp(INSERT_JOB_DETAIL)); + ps.setString(1, job.getName()); + ps.setString(2, job.getGroup()); + ps.setString(3, job.getDescription()); + ps.setString(4, job.getJobClass().getName()); + setBoolean(ps, 5, job.isDurable()); + setBoolean(ps, 6, job.isVolatile()); + setBoolean(ps, 7, job.isStateful()); + setBoolean(ps, 8, job.requestsRecovery()); + ps.setBinaryStream(9, bais, len); + + insertResult = ps.executeUpdate(); + } finally { + closeStatement(ps); + } + + if (insertResult > 0) { + String[] jobListeners = job.getJobListenerNames(); + for (int i = 0; jobListeners != null && i < jobListeners.length; i++) { + insertJobListener(conn, job, jobListeners[i]); + } + } + + return insertResult; + } + + /** + *+ * Update the job detail record. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to update + * @return number of rows updated + * @throws IOException + * if there were problems serializing the JobDataMap + */ + public int updateJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException { + //log.debug( "Updating job detail " + job ); + ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); + int len = baos.toByteArray().length; + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + + PreparedStatement ps = null; + + int insertResult = 0; + + try { + ps = conn.prepareStatement(rtp(UPDATE_JOB_DETAIL)); + ps.setString(1, job.getDescription()); + ps.setString(2, job.getJobClass().getName()); + setBoolean(ps, 3, job.isDurable()); + setBoolean(ps, 4, job.isVolatile()); + setBoolean(ps, 5, job.isStateful()); + setBoolean(ps, 6, job.requestsRecovery()); + ps.setBinaryStream(7, bais, len); + ps.setString(8, job.getName()); + ps.setString(9, job.getGroup()); + + insertResult = ps.executeUpdate(); + } finally { + closeStatement(ps); + } + + if (insertResult > 0) { + deleteJobListeners(conn, job.getName(), job.getGroup()); + + String[] jobListeners = job.getJobListenerNames(); + for (int i = 0; jobListeners != null && i < jobListeners.length; i++) { + insertJobListener(conn, job, jobListeners[i]); + } + } + + return insertResult; + } + + public int insertTrigger(Connection conn, Trigger trigger, String state, + JobDetail jobDetail) throws SQLException, IOException { + + ByteArrayOutputStream baos = serializeJobData(trigger.getJobDataMap()); + int len = baos.toByteArray().length; + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + + PreparedStatement ps = null; + + int insertResult = 0; + + try { + ps = conn.prepareStatement(rtp(INSERT_TRIGGER)); + ps.setString(1, trigger.getName()); + ps.setString(2, trigger.getGroup()); + ps.setString(3, trigger.getJobName()); + ps.setString(4, trigger.getJobGroup()); + setBoolean(ps, 5, trigger.isVolatile()); + ps.setString(6, trigger.getDescription()); + ps.setBigDecimal(7, new BigDecimal(String.valueOf(trigger + .getNextFireTime().getTime()))); + long prevFireTime = -1; + if (trigger.getPreviousFireTime() != null) { + prevFireTime = trigger.getPreviousFireTime().getTime(); + } + ps.setBigDecimal(8, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(9, state); + if (trigger instanceof SimpleTrigger && ((SimpleTrigger)trigger).hasAdditionalProperties() == false ) { + ps.setString(10, TTYPE_SIMPLE); + } else if (trigger instanceof CronTrigger && ((CronTrigger)trigger).hasAdditionalProperties() == false ) { + ps.setString(10, TTYPE_CRON); + } else { + ps.setString(10, TTYPE_BLOB); + } + ps.setBigDecimal(11, new BigDecimal(String.valueOf(trigger + .getStartTime().getTime()))); + long endTime = 0; + if (trigger.getEndTime() != null) { + endTime = trigger.getEndTime().getTime(); + } + ps.setBigDecimal(12, new BigDecimal(String.valueOf(endTime))); + ps.setString(13, trigger.getCalendarName()); + ps.setInt(14, trigger.getMisfireInstruction()); + ps.setBinaryStream(15, bais, len); + ps.setInt(16, trigger.getPriority()); + + insertResult = ps.executeUpdate(); + } finally { + closeStatement(ps); + } + + if (insertResult > 0) { + String[] trigListeners = trigger.getTriggerListenerNames(); + for (int i = 0; trigListeners != null && i < trigListeners.length; i++) { + insertTriggerListener(conn, trigger, trigListeners[i]); + } + } + + return insertResult; + } + + public int updateTrigger(Connection conn, Trigger trigger, String state, + JobDetail jobDetail) throws SQLException, IOException { + + ByteArrayOutputStream baos = serializeJobData(trigger.getJobDataMap()); + int len = baos.toByteArray().length; + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + + PreparedStatement ps = null; + + int insertResult = 0; + + + try { + ps = conn.prepareStatement(rtp(UPDATE_TRIGGER)); + + ps.setString(1, trigger.getJobName()); + ps.setString(2, trigger.getJobGroup()); + setBoolean(ps, 3, trigger.isVolatile()); + ps.setString(4, trigger.getDescription()); + long nextFireTime = -1; + if (trigger.getNextFireTime() != null) { + nextFireTime = trigger.getNextFireTime().getTime(); + } + ps.setBigDecimal(5, new BigDecimal(String.valueOf(nextFireTime))); + long prevFireTime = -1; + if (trigger.getPreviousFireTime() != null) { + prevFireTime = trigger.getPreviousFireTime().getTime(); + } + ps.setBigDecimal(6, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(7, state); + if (trigger instanceof SimpleTrigger && ((SimpleTrigger)trigger).hasAdditionalProperties() == false ) { + // updateSimpleTrigger(conn, (SimpleTrigger)trigger); + ps.setString(8, TTYPE_SIMPLE); + } else if (trigger instanceof CronTrigger && ((CronTrigger)trigger).hasAdditionalProperties() == false ) { + // updateCronTrigger(conn, (CronTrigger)trigger); + ps.setString(8, TTYPE_CRON); + } else { + // updateBlobTrigger(conn, trigger); + ps.setString(8, TTYPE_BLOB); + } + ps.setBigDecimal(9, new BigDecimal(String.valueOf(trigger + .getStartTime().getTime()))); + long endTime = 0; + if (trigger.getEndTime() != null) { + endTime = trigger.getEndTime().getTime(); + } + ps.setBigDecimal(10, new BigDecimal(String.valueOf(endTime))); + ps.setString(11, trigger.getCalendarName()); + ps.setInt(12, trigger.getMisfireInstruction()); + + ps.setInt(13, trigger.getPriority()); + ps.setBinaryStream(14, bais, len); + ps.setString(15, trigger.getName()); + ps.setString(16, trigger.getGroup()); + + insertResult = ps.executeUpdate(); + } finally { + closeStatement(ps); + } + + if (insertResult > 0) { + deleteTriggerListeners(conn, trigger.getName(), trigger.getGroup()); + + String[] trigListeners = trigger.getTriggerListenerNames(); + for (int i = 0; trigListeners != null && i < trigListeners.length; i++) { + insertTriggerListener(conn, trigger, trigListeners[i]); + } + } + + return insertResult; + } + + /** + *+ * Update the job data map for the given job. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to update + * @return the number of rows updated + */ + public int updateJobData(Connection conn, JobDetail job) + throws IOException, SQLException { + //log.debug( "Updating Job Data for Job " + job ); + ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); + int len = baos.toByteArray().length; + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_JOB_DATA)); + ps.setBinaryStream(1, bais, len); + ps.setString(2, job.getName()); + ps.setString(3, job.getGroup()); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + //--------------------------------------------------------------------------- + // triggers + //--------------------------------------------------------------------------- + + //--------------------------------------------------------------------------- + // calendars + //--------------------------------------------------------------------------- + + /** + *+ * Insert a new calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name for the new calendar + * @param calendar + * the calendar + * @return the number of rows inserted + * @throws IOException + * if there were problems serializing the calendar + */ + public int insertCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException { + //log.debug( "Inserting Calendar " + calendarName + " : " + calendar + // ); + ByteArrayOutputStream baos = serializeObject(calendar); + byte buf[] = baos.toByteArray(); + ByteArrayInputStream bais = new ByteArrayInputStream(buf); + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(INSERT_CALENDAR)); + ps.setString(1, calendarName); + ps.setBinaryStream(2, bais, buf.length); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update a calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name for the new calendar + * @param calendar + * the calendar + * @return the number of rows updated + * @throws IOException + * if there were problems serializing the calendar + */ + public int updateCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException { + //log.debug( "Updating calendar " + calendarName + " : " + calendar ); + ByteArrayOutputStream baos = serializeObject(calendar); + byte buf[] = baos.toByteArray(); + ByteArrayInputStream bais = new ByteArrayInputStream(buf); + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_CALENDAR)); + ps.setBinaryStream(1, bais, buf.length); + ps.setString(2, calendarName); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + //--------------------------------------------------------------------------- + // protected methods that can be overridden by subclasses + //--------------------------------------------------------------------------- + + /** + *
+ * This method should be overridden by any delegate subclasses that need
+ * special handling for BLOBs. The default implementation uses standard
+ * JDBC java.sql.Blob
operations.
+ *
+ * This method should be overridden by any delegate subclasses that need
+ * special handling for BLOBs for job details. The default implementation
+ * uses standard JDBC java.sql.Blob
operations.
+ *
+ * This is a driver delegate for the PostgreSQL JDBC driver. + *
+ * + * @author Jeffrey Wescott + */ +public class PostgreSQLDelegate extends StdJDBCDelegate { + /** + *+ * Create new PostgreSQLDelegate instance. + *
+ * + * @param log + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + */ + public PostgreSQLDelegate(Log log, String tablePrefix, String instanceId) { + super(log, tablePrefix, instanceId); + } + + /** + *+ * Create new PostgreSQLDelegate instance. + *
+ * + * @param log + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + * @param useProperties + * use java.util.Properties for storage + */ + public PostgreSQLDelegate(Log log, String tablePrefix, String instanceId, + Boolean useProperties) { + super(log, tablePrefix, instanceId, useProperties); + } + + //--------------------------------------------------------------------------- + // protected methods that can be overridden by subclasses + //--------------------------------------------------------------------------- + + /** + *
+ * This method should be overridden by any delegate subclasses that need
+ * special handling for BLOBs. The default implementation uses standard
+ * JDBC java.sql.Blob
operations.
+ *
+ * Conveys a scheduler-instance state record. + *
+ * + * @author James House + */ +public class SchedulerStateRecord implements java.io.Serializable { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private String schedulerInstanceId; + + private long checkinTimestamp; + + private long checkinInterval; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + */ + public long getCheckinInterval() { + return checkinInterval; + } + + /** + */ + public long getCheckinTimestamp() { + return checkinTimestamp; + } + + /** + */ + public String getSchedulerInstanceId() { + return schedulerInstanceId; + } + + /** + */ + public void setCheckinInterval(long l) { + checkinInterval = l; + } + + /** + */ + public void setCheckinTimestamp(long l) { + checkinTimestamp = l; + } + + /** + */ + public void setSchedulerInstanceId(String string) { + schedulerInstanceId = string; + } + +} + +// EOF diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/Semaphore.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/Semaphore.java new file mode 100644 index 000000000..081e5a9e0 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/Semaphore.java @@ -0,0 +1,79 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.impl.jdbcjobstore; + +import java.sql.Connection; + +/** + * An interface for providing thread/resource locking in order to protect + * resources from being altered by multiple threads at the same time. + * + * @author jhouse + */ +public interface Semaphore { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Grants a lock on the identified resource to the calling thread (blocking + * until it is available). + * + * @param conn Database connection used to establish lock. Can be null if + *{@link #requiresConnection()}
returns false.
+ *
+ * @return true if the lock was obtained.
+ */
+ boolean obtainLock(Connection conn, String lockName) throws LockException;
+
+ /**
+ * Release the lock on the identified resource if it is held by the calling
+ * thread.
+
+ * @param conn Database connection used to establish lock. Can be null if
+ * {@link #requiresConnection()}
returns false.
+ */
+ void releaseLock(Connection conn, String lockName) throws LockException;
+
+ /**
+ * Determine whether the calling thread owns a lock on the identified
+ * resource.
+
+ * @param conn Database connection used to establish lock. Can be null if
+ * {@link #requiresConnection()}
returns false.
+ */
+ boolean isLockOwner(Connection conn, String lockName) throws LockException;
+
+ /**
+ * Whether this Semaphore implementation requires a database connection for
+ * its lock management operations.
+ *
+ * @see #isLockOwner(Connection, String)
+ * @see #obtainLock(Connection, String)
+ * @see #releaseLock(Connection, String)
+ */
+ boolean requiresConnection();
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/SimpleSemaphore.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/SimpleSemaphore.java
new file mode 100644
index 000000000..8c3c79183
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/SimpleSemaphore.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import java.sql.Connection;
+import java.util.HashSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Internal in-memory lock handler for providing thread/resource locking in
+ * order to protect resources from being altered by multiple threads at the
+ * same time.
+ *
+ * @author jhouse
+ */
+public class SimpleSemaphore implements Semaphore {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ ThreadLocal lockOwners = new ThreadLocal();
+
+ HashSet locks = new HashSet();
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ protected Log getLog() {
+ return log;
+ }
+
+ private HashSet getThreadLocks() {
+ HashSet threadLocks = (HashSet) lockOwners.get();
+ if (threadLocks == null) {
+ threadLocks = new HashSet();
+ lockOwners.set(threadLocks);
+ }
+ return threadLocks;
+ }
+
+ /**
+ * Grants a lock on the identified resource to the calling thread (blocking
+ * until it is available).
+ *
+ * @return true if the lock was obtained.
+ */
+ public synchronized boolean obtainLock(Connection conn, String lockName) {
+
+ lockName = lockName.intern();
+
+ Log log = getLog();
+
+ if(log.isDebugEnabled()) {
+ log.debug(
+ "Lock '" + lockName + "' is desired by: "
+ + Thread.currentThread().getName());
+ }
+
+ if (!isLockOwner(conn, lockName)) {
+ if(log.isDebugEnabled()) {
+ log.debug(
+ "Lock '" + lockName + "' is being obtained: "
+ + Thread.currentThread().getName());
+ }
+ while (locks.contains(lockName)) {
+ try {
+ this.wait();
+ } catch (InterruptedException ie) {
+ if(log.isDebugEnabled()) {
+ log.debug(
+ "Lock '" + lockName + "' was not obtained by: "
+ + Thread.currentThread().getName());
+ }
+ }
+ }
+
+ if(log.isDebugEnabled()) {
+ log.debug(
+ "Lock '" + lockName + "' given to: "
+ + Thread.currentThread().getName());
+ }
+ getThreadLocks().add(lockName);
+ locks.add(lockName);
+ } else if(log.isDebugEnabled()) {
+ log.debug(
+ "Lock '" + lockName + "' already owned by: "
+ + Thread.currentThread().getName()
+ + " -- but not owner!",
+ new Exception("stack-trace of wrongful returner"));
+ }
+
+ return true;
+ }
+
+ /**
+ * Release the lock on the identified resource if it is held by the calling
+ * thread.
+ */
+ public synchronized void releaseLock(Connection conn, String lockName) {
+
+ lockName = lockName.intern();
+
+ if (isLockOwner(conn, lockName)) {
+ if(getLog().isDebugEnabled()) {
+ getLog().debug(
+ "Lock '" + lockName + "' retuned by: "
+ + Thread.currentThread().getName());
+ }
+ getThreadLocks().remove(lockName);
+ locks.remove(lockName);
+ this.notifyAll();
+ } else if (getLog().isDebugEnabled()) {
+ getLog().debug(
+ "Lock '" + lockName + "' attempt to retun by: "
+ + Thread.currentThread().getName()
+ + " -- but not owner!",
+ new Exception("stack-trace of wrongful returner"));
+ }
+ }
+
+ /**
+ * Determine whether the calling thread owns a lock on the identified
+ * resource.
+ */
+ public synchronized boolean isLockOwner(Connection conn, String lockName) {
+
+ lockName = lockName.intern();
+
+ return getThreadLocks().contains(lockName);
+ }
+
+ /**
+ * This Semaphore implementation does not use the database.
+ */
+ public boolean requiresConnection() {
+ return false;
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/StdJDBCConstants.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/StdJDBCConstants.java
new file mode 100644
index 000000000..ceb6413e9
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/StdJDBCConstants.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+/**
+ *
+ * This interface extends {@link
+ * com.fr.third.org.quartz.impl.jdbcjobstore.Constants}
+ * to include the query string constants in use by the {@link
+ * com.fr.third.org.quartz.impl.jdbcjobstore.StdJDBCDelegate}
+ * class.
+ *
+ * This is meant to be an abstract base class for most, if not all, {@link com.fr.third.org.quartz.impl.jdbcjobstore.DriverDelegate}
+ * implementations. Subclasses should override only those methods that need
+ * special handling for the DBMS driver in question.
+ *
+ * Create new StdJDBCDelegate instance. + *
+ * + * @param logger + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + */ + public StdJDBCDelegate(Log logger, String tablePrefix, String instanceId) { + this.logger = logger; + this.tablePrefix = tablePrefix; + this.instanceId = instanceId; + } + + /** + *+ * Create new StdJDBCDelegate instance. + *
+ * + * @param logger + * the logger to use during execution + * @param tablePrefix + * the prefix of all table names + */ + public StdJDBCDelegate(Log logger, String tablePrefix, String instanceId, + Boolean useProperties) { + this.logger = logger; + this.tablePrefix = tablePrefix; + this.instanceId = instanceId; + this.useProperties = useProperties.booleanValue(); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + protected boolean canUseProperties() { + return useProperties; + } + + //--------------------------------------------------------------------------- + // startup / recovery + //--------------------------------------------------------------------------- + + /** + *+ * Insert the job detail record. + *
+ * + * @param conn + * the DB Connection + * @param newState + * the new state for the triggers + * @param oldState1 + * the first old state to update + * @param oldState2 + * the second old state to update + * @return number of rows updated + */ + public int updateTriggerStatesFromOtherStates(Connection conn, + String newState, String oldState1, String oldState2) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn + .prepareStatement(rtp(UPDATE_TRIGGER_STATES_FROM_OTHER_STATES)); + ps.setString(1, newState); + ps.setString(2, oldState1); + ps.setString(3, oldState2); + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Get the names of all of the triggers that have misfired. + *
+ * + * @param conn + * the DB Connection + * @return an array of{@link
+ * com.fr.third.org.quartz.utils.Key}
objects
+ */
+ public Key[] selectMisfiredTriggers(Connection conn, long ts)
+ throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS));
+ ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts)));
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ String triggerName = rs.getString(COL_TRIGGER_NAME);
+ String groupName = rs.getString(COL_TRIGGER_GROUP);
+ list.add(new Key(triggerName, groupName));
+ }
+ Object[] oArr = list.toArray();
+ Key[] kArr = new Key[oArr.length];
+ System.arraycopy(oArr, 0, kArr, 0, oArr.length);
+ return kArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Select all of the triggers in a given state. + *
+ * + * @param conn + * the DB Connection + * @param state + * the state the triggers must be in + * @return an array of triggerKey
s
+ */
+ public Key[] selectTriggersInState(Connection conn, String state)
+ throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_IN_STATE));
+ ps.setString(1, state);
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ list.add(new Key(rs.getString(1), rs.getString(2)));
+ }
+
+ Key[] sArr = (Key[]) list.toArray(new Key[list.size()]);
+ return sArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ public Key[] selectMisfiredTriggersInState(Connection conn, String state,
+ long ts) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS_IN_STATE));
+ ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts)));
+ ps.setString(2, state);
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ String triggerName = rs.getString(COL_TRIGGER_NAME);
+ String groupName = rs.getString(COL_TRIGGER_GROUP);
+ list.add(new Key(triggerName, groupName));
+ }
+ Object[] oArr = list.toArray();
+ Key[] kArr = new Key[oArr.length];
+ System.arraycopy(oArr, 0, kArr, 0, oArr.length);
+ return kArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Get the names of all of the triggers in the given states that have + * misfired - according to the given timestamp. No more than count will + * be returned. + *
+ * + * @param conn The DB Connection + * @param count The most misfired triggers to return, negative for all + * @param resultList Output parameter. A List of + *{@link com.fr.third.org.quartz.utils.Key}
objects. Must not be null.
+ *
+ * @return Whether there are more misfired triggers left to find beyond
+ * the given count.
+ */
+ public boolean selectMisfiredTriggersInStates(Connection conn, String state1, String state2,
+ long ts, int count, List resultList) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS_IN_STATES));
+ ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts)));
+ ps.setString(2, state1);
+ ps.setString(3, state2);
+ rs = ps.executeQuery();
+
+ boolean hasReachedLimit = false;
+ while (rs.next() && (hasReachedLimit == false)) {
+ if (resultList.size() == count) {
+ hasReachedLimit = true;
+ } else {
+ String triggerName = rs.getString(COL_TRIGGER_NAME);
+ String groupName = rs.getString(COL_TRIGGER_GROUP);
+ resultList.add(new Key(triggerName, groupName));
+ }
+ }
+
+ return hasReachedLimit;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Get the number of triggers in the given states that have + * misfired - according to the given timestamp. + *
+ * + * @param conn the DB Connection + */ + public int countMisfiredTriggersInStates( + Connection conn, String state1, String state2, long ts) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(COUNT_MISFIRED_TRIGGERS_IN_STATES)); + ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts))); + ps.setString(2, state1); + ps.setString(3, state2); + rs = ps.executeQuery(); + + if (rs.next()) { + return rs.getInt(1); + } + + throw new SQLException("No misfired trigger count returned."); + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Get the names of all of the triggers in the given group and state that + * have misfired. + *
+ * + * @param conn + * the DB Connection + * @return an array of{@link
+ * com.fr.third.org.quartz.utils.Key}
objects
+ */
+ public Key[] selectMisfiredTriggersInGroupInState(Connection conn,
+ String groupName, String state, long ts) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn
+ .prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS_IN_GROUP_IN_STATE));
+ ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts)));
+ ps.setString(2, groupName);
+ ps.setString(3, state);
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ String triggerName = rs.getString(COL_TRIGGER_NAME);
+ list.add(new Key(triggerName, groupName));
+ }
+ Object[] oArr = list.toArray();
+ Key[] kArr = new Key[oArr.length];
+ System.arraycopy(oArr, 0, kArr, 0, oArr.length);
+ return kArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ *
+ * Select all of the triggers for jobs that are requesting recovery. The
+ * returned trigger objects will have unique "recoverXXX" trigger names and
+ * will be in the {@link
+ * com.fr.third.org.quartz.Scheduler}.DEFAULT_RECOVERY_GROUP
+ * trigger group.
+ *
+ * In order to preserve the ordering of the triggers, the fire time will be
+ * set from the COL_FIRED_TIME
column in the TABLE_FIRED_TRIGGERS
+ * table. The caller is responsible for calling computeFirstFireTime
+ * on each returned trigger. It is also up to the caller to insert the
+ * returned triggers to ensure that they are fired.
+ *
{@link com.fr.third.org.quartz.Trigger}
objects
+ */
+ public Trigger[] selectTriggersForRecoveringJobs(Connection conn)
+ throws SQLException, IOException, ClassNotFoundException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn
+ .prepareStatement(rtp(SELECT_INSTANCES_RECOVERABLE_FIRED_TRIGGERS));
+ ps.setString(1, instanceId);
+ setBoolean(ps, 2, true);
+ rs = ps.executeQuery();
+
+ long dumId = System.currentTimeMillis();
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ String jobName = rs.getString(COL_JOB_NAME);
+ String jobGroup = rs.getString(COL_JOB_GROUP);
+ String trigName = rs.getString(COL_TRIGGER_NAME);
+ String trigGroup = rs.getString(COL_TRIGGER_GROUP);
+ long firedTime = rs.getLong(COL_FIRED_TIME);
+ int priority = rs.getInt(COL_PRIORITY);
+ SimpleTrigger rcvryTrig = new SimpleTrigger("recover_"
+ + instanceId + "_" + String.valueOf(dumId++),
+ Scheduler.DEFAULT_RECOVERY_GROUP, new Date(firedTime));
+ rcvryTrig.setJobName(jobName);
+ rcvryTrig.setJobGroup(jobGroup);
+ rcvryTrig.setPriority(priority);
+ rcvryTrig
+ .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
+
+ JobDataMap jd = selectTriggerJobDataMap(conn, trigName, trigGroup);
+ jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_NAME, trigName);
+ jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_GROUP, trigGroup);
+ jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_FIRETIME_IN_MILLISECONDS, String.valueOf(firedTime));
+ rcvryTrig.setJobDataMap(jd);
+
+ list.add(rcvryTrig);
+ }
+ Object[] oArr = list.toArray();
+ Trigger[] tArr = new Trigger[oArr.length];
+ System.arraycopy(oArr, 0, tArr, 0, oArr.length);
+ return tArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Delete all fired triggers. + *
+ * + * @param conn + * the DB Connection + * @return the number of rows deleted + */ + public int deleteFiredTriggers(Connection conn) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_FIRED_TRIGGERS)); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + public int deleteFiredTriggers(Connection conn, String instanceId) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_INSTANCES_FIRED_TRIGGERS)); + ps.setString(1, instanceId); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + //--------------------------------------------------------------------------- + // jobs + //--------------------------------------------------------------------------- + + /** + *+ * Insert the job detail record. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to insert + * @return number of rows inserted + * @throws IOException + * if there were problems serializing the JobDataMap + */ + public int insertJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException { + ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); + + PreparedStatement ps = null; + + int insertResult = 0; + + try { + ps = conn.prepareStatement(rtp(INSERT_JOB_DETAIL)); + ps.setString(1, job.getName()); + ps.setString(2, job.getGroup()); + ps.setString(3, job.getDescription()); + ps.setString(4, job.getJobClass().getName()); + setBoolean(ps, 5, job.isDurable()); + setBoolean(ps, 6, job.isVolatile()); + setBoolean(ps, 7, job.isStateful()); + setBoolean(ps, 8, job.requestsRecovery()); + setBytes(ps, 9, baos); + + insertResult = ps.executeUpdate(); + } finally { + closeStatement(ps); + } + + if (insertResult > 0) { + String[] jobListeners = job.getJobListenerNames(); + for (int i = 0; jobListeners != null && i < jobListeners.length; i++) { + insertJobListener(conn, job, jobListeners[i]); + } + } + + return insertResult; + } + + /** + *+ * Update the job detail record. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to update + * @return number of rows updated + * @throws IOException + * if there were problems serializing the JobDataMap + */ + public int updateJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException { + ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); + + PreparedStatement ps = null; + + int insertResult = 0; + + try { + ps = conn.prepareStatement(rtp(UPDATE_JOB_DETAIL)); + ps.setString(1, job.getDescription()); + ps.setString(2, job.getJobClass().getName()); + setBoolean(ps, 3, job.isDurable()); + setBoolean(ps, 4, job.isVolatile()); + setBoolean(ps, 5, job.isStateful()); + setBoolean(ps, 6, job.requestsRecovery()); + setBytes(ps, 7, baos); + ps.setString(8, job.getName()); + ps.setString(9, job.getGroup()); + + insertResult = ps.executeUpdate(); + } finally { + closeStatement(ps); + } + + if (insertResult > 0) { + deleteJobListeners(conn, job.getName(), job.getGroup()); + + String[] jobListeners = job.getJobListenerNames(); + for (int i = 0; jobListeners != null && i < jobListeners.length; i++) { + insertJobListener(conn, job, jobListeners[i]); + } + } + + return insertResult; + } + + /** + *+ * Get the names of all of the triggers associated with the given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return an array of{@link
+ * com.fr.third.org.quartz.utils.Key}
objects
+ */
+ public Key[] selectTriggerNamesForJob(Connection conn, String jobName,
+ String groupName) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_FOR_JOB));
+ ps.setString(1, jobName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList(10);
+ while (rs.next()) {
+ String trigName = rs.getString(COL_TRIGGER_NAME);
+ String trigGroup = rs.getString(COL_TRIGGER_GROUP);
+ list.add(new Key(trigName, trigGroup));
+ }
+ Object[] oArr = list.toArray();
+ Key[] kArr = new Key[oArr.length];
+ System.arraycopy(oArr, 0, kArr, 0, oArr.length);
+ return kArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Delete all job listeners for the given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return the number of rows deleted + */ + public int deleteJobListeners(Connection conn, String jobName, + String groupName) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_JOB_LISTENERS)); + ps.setString(1, jobName); + ps.setString(2, groupName); + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Delete the job detail record for the given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return the number of rows deleted + */ + public int deleteJobDetail(Connection conn, String jobName, String groupName) + throws SQLException { + PreparedStatement ps = null; + + try { + if (logger.isDebugEnabled()) { + logger.debug("Deleting job: " + groupName + "." + jobName); + } + ps = conn.prepareStatement(rtp(DELETE_JOB_DETAIL)); + ps.setString(1, jobName); + ps.setString(2, groupName); + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Check whether or not the given job is stateful. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return true if the job exists and is stateful, false otherwise + */ + public boolean isJobStateful(Connection conn, String jobName, + String groupName) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_JOB_STATEFUL)); + ps.setString(1, jobName); + ps.setString(2, groupName); + rs = ps.executeQuery(); + if (!rs.next()) { return false; } + return getBoolean(rs, COL_IS_STATEFUL); + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Check whether or not the given job exists. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return true if the job exists, false otherwise + */ + public boolean jobExists(Connection conn, String jobName, String groupName) + throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_JOB_EXISTENCE)); + ps.setString(1, jobName); + ps.setString(2, groupName); + rs = ps.executeQuery(); + if (rs.next()) { + return true; + } else { + return false; + } + } finally { + closeResultSet(rs); + closeStatement(ps); + } + + } + + /** + *+ * Update the job data map for the given job. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to update + * @return the number of rows updated + */ + public int updateJobData(Connection conn, JobDetail job) + throws IOException, SQLException { + ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_JOB_DATA)); + setBytes(ps, 1, baos); + ps.setString(2, job.getName()); + ps.setString(3, job.getGroup()); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Associate a listener with a job. + *
+ * + * @param conn + * the DB Connection + * @param job + * the job to associate with the listener + * @param listener + * the listener to insert + * @return the number of rows inserted + */ + public int insertJobListener(Connection conn, JobDetail job, String listener) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(INSERT_JOB_LISTENER)); + ps.setString(1, job.getName()); + ps.setString(2, job.getGroup()); + ps.setString(3, listener); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Get all of the listeners for a given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the job name whose listeners are wanted + * @param groupName + * the group containing the job + * @return array ofString
listener names
+ */
+ public String[] selectJobListeners(Connection conn, String jobName,
+ String groupName) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ArrayList list = new ArrayList();
+ ps = conn.prepareStatement(rtp(SELECT_JOB_LISTENERS));
+ ps.setString(1, jobName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ while (rs.next()) {
+ list.add(rs.getString(1));
+ }
+
+ Object[] oArr = list.toArray();
+ String[] sArr = new String[oArr.length];
+ System.arraycopy(oArr, 0, sArr, 0, oArr.length);
+ return sArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Select the JobDetail object for a given job name / group name. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the job name whose listeners are wanted + * @param groupName + * the group containing the job + * @return the populated JobDetail object + * @throws ClassNotFoundException + * if a class found during deserialization cannot be found or if + * the job class could not be found + * @throws IOException + * if deserialization causes an error + */ + public JobDetail selectJobDetail(Connection conn, String jobName, + String groupName, ClassLoadHelper loadHelper) + throws ClassNotFoundException, IOException, SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_JOB_DETAIL)); + ps.setString(1, jobName); + ps.setString(2, groupName); + rs = ps.executeQuery(); + + JobDetail job = null; + + if (rs.next()) { + job = new JobDetail(); + + job.setName(rs.getString(COL_JOB_NAME)); + job.setGroup(rs.getString(COL_JOB_GROUP)); + job.setDescription(rs.getString(COL_DESCRIPTION)); + job.setJobClass(loadHelper.loadClass(rs + .getString(COL_JOB_CLASS))); + job.setDurability(getBoolean(rs, COL_IS_DURABLE)); + job.setVolatility(getBoolean(rs, COL_IS_VOLATILE)); + job.setRequestsRecovery(getBoolean(rs, COL_REQUESTS_RECOVERY)); + + Map map = null; + if (canUseProperties()) { + map = getMapFromProperties(rs); + } else { + map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP); + } + + if (null != map) { + job.setJobDataMap(new JobDataMap(map)); + } + } + + return job; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + * build Map from java.util.Properties encoding. + */ + private Map getMapFromProperties(ResultSet rs) + throws ClassNotFoundException, IOException, SQLException { + Map map; + InputStream is = (InputStream) getJobDetailFromBlob(rs, COL_JOB_DATAMAP); + if(is == null) { + return null; + } + Properties properties = new Properties(); + if (is != null) { + try { + properties.load(is); + } finally { + is.close(); + } + } + map = convertFromProperty(properties); + return map; + } + + /** + *+ * Select the total number of jobs stored. + *
+ * + * @param conn + * the DB Connection + * @return the total number of jobs stored + */ + public int selectNumJobs(Connection conn) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + int count = 0; + ps = conn.prepareStatement(rtp(SELECT_NUM_JOBS)); + rs = ps.executeQuery(); + + if (rs.next()) { + count = rs.getInt(1); + } + + return count; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Select all of the job group names that are stored. + *
+ * + * @param conn + * the DB Connection + * @return an array ofString
group names
+ */
+ public String[] selectJobGroups(Connection conn) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_JOB_GROUPS));
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ list.add(rs.getString(1));
+ }
+
+ Object[] oArr = list.toArray();
+ String[] sArr = new String[oArr.length];
+ System.arraycopy(oArr, 0, sArr, 0, oArr.length);
+ return sArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Select all of the jobs contained in a given group. + *
+ * + * @param conn + * the DB Connection + * @param groupName + * the group containing the jobs + * @return an array ofString
job names
+ */
+ public String[] selectJobsInGroup(Connection conn, String groupName)
+ throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_JOBS_IN_GROUP));
+ ps.setString(1, groupName);
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ list.add(rs.getString(1));
+ }
+
+ Object[] oArr = list.toArray();
+ String[] sArr = new String[oArr.length];
+ System.arraycopy(oArr, 0, sArr, 0, oArr.length);
+ return sArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ //---------------------------------------------------------------------------
+ // triggers
+ //---------------------------------------------------------------------------
+
+ /**
+ * + * Insert the base trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @param state + * the state that the trigger should be stored in + * @return the number of rows inserted + */ + public int insertTrigger(Connection conn, Trigger trigger, String state, + JobDetail jobDetail) throws SQLException, IOException { + + ByteArrayOutputStream baos = null; + if(trigger.getJobDataMap().size() > 0) { + baos = serializeJobData(trigger.getJobDataMap()); + } + + PreparedStatement ps = null; + + int insertResult = 0; + + try { + ps = conn.prepareStatement(rtp(INSERT_TRIGGER)); + ps.setString(1, trigger.getName()); + ps.setString(2, trigger.getGroup()); + ps.setString(3, trigger.getJobName()); + ps.setString(4, trigger.getJobGroup()); + setBoolean(ps, 5, trigger.isVolatile()); + ps.setString(6, trigger.getDescription()); + if(trigger.getNextFireTime() != null) + ps.setBigDecimal(7, new BigDecimal(String.valueOf(trigger + .getNextFireTime().getTime()))); + else + ps.setBigDecimal(7, null); + long prevFireTime = -1; + if (trigger.getPreviousFireTime() != null) { + prevFireTime = trigger.getPreviousFireTime().getTime(); + } + ps.setBigDecimal(8, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(9, state); + if (trigger instanceof SimpleTrigger && ((SimpleTrigger)trigger).hasAdditionalProperties() == false ) { + ps.setString(10, TTYPE_SIMPLE); + } else if (trigger instanceof CronTrigger && ((CronTrigger)trigger).hasAdditionalProperties() == false ) { + ps.setString(10, TTYPE_CRON); + } else { + ps.setString(10, TTYPE_BLOB); + } + ps.setBigDecimal(11, new BigDecimal(String.valueOf(trigger + .getStartTime().getTime()))); + long endTime = 0; + if (trigger.getEndTime() != null) { + endTime = trigger.getEndTime().getTime(); + } + ps.setBigDecimal(12, new BigDecimal(String.valueOf(endTime))); + ps.setString(13, trigger.getCalendarName()); + ps.setInt(14, trigger.getMisfireInstruction()); + setBytes(ps, 15, baos); + ps.setInt(16, trigger.getPriority()); + + insertResult = ps.executeUpdate(); + } finally { + closeStatement(ps); + } + + if (insertResult > 0) { + String[] trigListeners = trigger.getTriggerListenerNames(); + for (int i = 0; trigListeners != null && i < trigListeners.length; i++) { + insertTriggerListener(conn, trigger, trigListeners[i]); + } + } + + return insertResult; + } + + /** + *+ * Insert the simple trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows inserted + */ + public int insertSimpleTrigger(Connection conn, SimpleTrigger trigger) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(INSERT_SIMPLE_TRIGGER)); + ps.setString(1, trigger.getName()); + ps.setString(2, trigger.getGroup()); + ps.setInt(3, trigger.getRepeatCount()); + ps.setBigDecimal(4, new BigDecimal(String.valueOf(trigger + .getRepeatInterval()))); + ps.setInt(5, trigger.getTimesTriggered()); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Insert the cron trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows inserted + */ + public int insertCronTrigger(Connection conn, CronTrigger trigger) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(INSERT_CRON_TRIGGER)); + ps.setString(1, trigger.getName()); + ps.setString(2, trigger.getGroup()); + ps.setString(3, trigger.getCronExpression()); + ps.setString(4, trigger.getTimeZone().getID()); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Insert the blob trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows inserted + */ + public int insertBlobTrigger(Connection conn, Trigger trigger) + throws SQLException, IOException { + PreparedStatement ps = null; + ByteArrayOutputStream os = null; + + try { + // update the blob + os = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(os); + oos.writeObject(trigger); + oos.close(); + + byte[] buf = os.toByteArray(); + ByteArrayInputStream is = new ByteArrayInputStream(buf); + + ps = conn.prepareStatement(rtp(INSERT_BLOB_TRIGGER)); + ps.setString(1, trigger.getName()); + ps.setString(2, trigger.getGroup()); + ps.setBinaryStream(3, is, buf.length); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update the base trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @param state + * the state that the trigger should be stored in + * @return the number of rows updated + */ + public int updateTrigger(Connection conn, Trigger trigger, String state, + JobDetail jobDetail) throws SQLException, IOException { + + // save some clock cycles by unnecessarily writing job data blob ... + boolean updateJobData = trigger.getJobDataMap().isDirty(); + ByteArrayOutputStream baos = null; + if(updateJobData && trigger.getJobDataMap().size() > 0) { + baos = serializeJobData(trigger.getJobDataMap()); + } + + PreparedStatement ps = null; + + int insertResult = 0; + + + try { + if(updateJobData) { + ps = conn.prepareStatement(rtp(UPDATE_TRIGGER)); + } else { + ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_SKIP_DATA)); + } + + ps.setString(1, trigger.getJobName()); + ps.setString(2, trigger.getJobGroup()); + setBoolean(ps, 3, trigger.isVolatile()); + ps.setString(4, trigger.getDescription()); + long nextFireTime = -1; + if (trigger.getNextFireTime() != null) { + nextFireTime = trigger.getNextFireTime().getTime(); + } + ps.setBigDecimal(5, new BigDecimal(String.valueOf(nextFireTime))); + long prevFireTime = -1; + if (trigger.getPreviousFireTime() != null) { + prevFireTime = trigger.getPreviousFireTime().getTime(); + } + ps.setBigDecimal(6, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(7, state); + if (trigger instanceof SimpleTrigger && ((SimpleTrigger)trigger).hasAdditionalProperties() == false ) { + // updateSimpleTrigger(conn, (SimpleTrigger)trigger); + ps.setString(8, TTYPE_SIMPLE); + } else if (trigger instanceof CronTrigger && ((CronTrigger)trigger).hasAdditionalProperties() == false ) { + // updateCronTrigger(conn, (CronTrigger)trigger); + ps.setString(8, TTYPE_CRON); + } else { + // updateBlobTrigger(conn, trigger); + ps.setString(8, TTYPE_BLOB); + } + ps.setBigDecimal(9, new BigDecimal(String.valueOf(trigger + .getStartTime().getTime()))); + long endTime = 0; + if (trigger.getEndTime() != null) { + endTime = trigger.getEndTime().getTime(); + } + ps.setBigDecimal(10, new BigDecimal(String.valueOf(endTime))); + ps.setString(11, trigger.getCalendarName()); + ps.setInt(12, trigger.getMisfireInstruction()); + ps.setInt(13, trigger.getPriority()); + + if(updateJobData) { + setBytes(ps, 14, baos); + ps.setString(15, trigger.getName()); + ps.setString(16, trigger.getGroup()); + } else { + ps.setString(14, trigger.getName()); + ps.setString(15, trigger.getGroup()); + } + + insertResult = ps.executeUpdate(); + } finally { + closeStatement(ps); + } + + if (insertResult > 0) { + deleteTriggerListeners(conn, trigger.getName(), trigger.getGroup()); + + String[] trigListeners = trigger.getTriggerListenerNames(); + for (int i = 0; trigListeners != null && i < trigListeners.length; i++) { + insertTriggerListener(conn, trigger, trigListeners[i]); + } + } + + return insertResult; + } + + /** + *+ * Update the simple trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows updated + */ + public int updateSimpleTrigger(Connection conn, SimpleTrigger trigger) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_SIMPLE_TRIGGER)); + + ps.setInt(1, trigger.getRepeatCount()); + ps.setBigDecimal(2, new BigDecimal(String.valueOf(trigger + .getRepeatInterval()))); + ps.setInt(3, trigger.getTimesTriggered()); + ps.setString(4, trigger.getName()); + ps.setString(5, trigger.getGroup()); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update the cron trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows updated + */ + public int updateCronTrigger(Connection conn, CronTrigger trigger) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_CRON_TRIGGER)); + ps.setString(1, trigger.getCronExpression()); + ps.setString(2, trigger.getName()); + ps.setString(3, trigger.getGroup()); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update the blob trigger data. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger to insert + * @return the number of rows updated + */ + public int updateBlobTrigger(Connection conn, Trigger trigger) + throws SQLException, IOException { + PreparedStatement ps = null; + ByteArrayOutputStream os = null; + + try { + // update the blob + os = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(os); + oos.writeObject(trigger); + oos.close(); + + byte[] buf = os.toByteArray(); + ByteArrayInputStream is = new ByteArrayInputStream(buf); + + ps = conn.prepareStatement(rtp(UPDATE_BLOB_TRIGGER)); + ps.setBinaryStream(1, is, buf.length); + ps.setString(2, trigger.getName()); + ps.setString(3, trigger.getGroup()); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + if (os != null) { + os.close(); + } + } + } + + /** + *+ * Check whether or not a trigger exists. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return true if the trigger exists, false otherwise + */ + public boolean triggerExists(Connection conn, String triggerName, + String groupName) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_TRIGGER_EXISTENCE)); + ps.setString(1, triggerName); + ps.setString(2, groupName); + rs = ps.executeQuery(); + + if (rs.next()) { + return true; + } else { + return false; + } + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Update the state for a given trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @param state + * the new state for the trigger + * @return the number of rows updated + */ + public int updateTriggerState(Connection conn, String triggerName, + String groupName, String state) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_STATE)); + ps.setString(1, state); + ps.setString(2, triggerName); + ps.setString(3, groupName); + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update the given trigger to the given new state, if it is one of the + * given old states. + *
+ * + * @param conn + * the DB connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @param newState + * the new state for the trigger + * @param oldState1 + * one of the old state the trigger must be in + * @param oldState2 + * one of the old state the trigger must be in + * @param oldState3 + * one of the old state the trigger must be in + * @return int the number of rows updated + * @throws SQLException + */ + public int updateTriggerStateFromOtherStates(Connection conn, + String triggerName, String groupName, String newState, + String oldState1, String oldState2, String oldState3) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_STATE_FROM_STATES)); + ps.setString(1, newState); + ps.setString(2, triggerName); + ps.setString(3, groupName); + ps.setString(4, oldState1); + ps.setString(5, oldState2); + ps.setString(6, oldState3); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + public int updateTriggerStateFromOtherStatesBeforeTime(Connection conn, + String newState, String oldState1, String oldState2, long time) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn + .prepareStatement(rtp(UPDATE_TRIGGER_STATE_FROM_OTHER_STATES_BEFORE_TIME)); + ps.setString(1, newState); + ps.setString(2, oldState1); + ps.setString(3, oldState2); + ps.setLong(4, time); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update all triggers in the given group to the given new state, if they + * are in one of the given old states. + *
+ * + * @param conn + * the DB connection + * @param groupName + * the group containing the trigger + * @param newState + * the new state for the trigger + * @param oldState1 + * one of the old state the trigger must be in + * @param oldState2 + * one of the old state the trigger must be in + * @param oldState3 + * one of the old state the trigger must be in + * @return int the number of rows updated + * @throws SQLException + */ + public int updateTriggerGroupStateFromOtherStates(Connection conn, + String groupName, String newState, String oldState1, + String oldState2, String oldState3) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn + .prepareStatement(rtp(UPDATE_TRIGGER_GROUP_STATE_FROM_STATES)); + ps.setString(1, newState); + ps.setString(2, groupName); + ps.setString(3, oldState1); + ps.setString(4, oldState2); + ps.setString(5, oldState3); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update the given trigger to the given new state, if it is in the given + * old state. + *
+ * + * @param conn + * the DB connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @param newState + * the new state for the trigger + * @param oldState + * the old state the trigger must be in + * @return int the number of rows updated + * @throws SQLException + */ + public int updateTriggerStateFromOtherState(Connection conn, + String triggerName, String groupName, String newState, + String oldState) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_STATE_FROM_STATE)); + ps.setString(1, newState); + ps.setString(2, triggerName); + ps.setString(3, groupName); + ps.setString(4, oldState); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update all of the triggers of the given group to the given new state, if + * they are in the given old state. + *
+ * + * @param conn + * the DB connection + * @param groupName + * the group containing the triggers + * @param newState + * the new state for the trigger group + * @param oldState + * the old state the triggers must be in + * @return int the number of rows updated + * @throws SQLException + */ + public int updateTriggerGroupStateFromOtherState(Connection conn, + String groupName, String newState, String oldState) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn + .prepareStatement(rtp(UPDATE_TRIGGER_GROUP_STATE_FROM_STATE)); + ps.setString(1, newState); + ps.setString(2, groupName); + ps.setString(3, oldState); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update the states of all triggers associated with the given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @param state + * the new state for the triggers + * @return the number of rows updated + */ + public int updateTriggerStatesForJob(Connection conn, String jobName, + String groupName, String state) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_JOB_TRIGGER_STATES)); + ps.setString(1, state); + ps.setString(2, jobName); + ps.setString(3, groupName); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + public int updateTriggerStatesForJobFromOtherState(Connection conn, + String jobName, String groupName, String state, String oldState) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn + .prepareStatement(rtp(UPDATE_JOB_TRIGGER_STATES_FROM_OTHER_STATE)); + ps.setString(1, state); + ps.setString(2, jobName); + ps.setString(3, groupName); + ps.setString(4, oldState); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Delete all of the listeners associated with a given trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger whose listeners will be deleted + * @param groupName + * the name of the group containing the trigger + * @return the number of rows deleted + */ + public int deleteTriggerListeners(Connection conn, String triggerName, + String groupName) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_TRIGGER_LISTENERS)); + ps.setString(1, triggerName); + ps.setString(2, groupName); + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Associate a listener with the given trigger. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger + * @param listener + * the name of the listener to associate with the trigger + * @return the number of rows inserted + */ + public int insertTriggerListener(Connection conn, Trigger trigger, + String listener) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(INSERT_TRIGGER_LISTENER)); + ps.setString(1, trigger.getName()); + ps.setString(2, trigger.getGroup()); + ps.setString(3, listener); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Select the listeners associated with a given trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return array ofString
trigger listener names
+ */
+ public String[] selectTriggerListeners(Connection conn, String triggerName,
+ String groupName) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGER_LISTENERS));
+ ps.setString(1, triggerName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ list.add(rs.getString(1));
+ }
+ Object[] oArr = list.toArray();
+ String[] sArr = new String[oArr.length];
+ System.arraycopy(oArr, 0, sArr, 0, oArr.length);
+ return sArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Delete the simple trigger data for a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the number of rows deleted + */ + public int deleteSimpleTrigger(Connection conn, String triggerName, + String groupName) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_SIMPLE_TRIGGER)); + ps.setString(1, triggerName); + ps.setString(2, groupName); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Delete the cron trigger data for a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the number of rows deleted + */ + public int deleteCronTrigger(Connection conn, String triggerName, + String groupName) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_CRON_TRIGGER)); + ps.setString(1, triggerName); + ps.setString(2, groupName); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Delete the cron trigger data for a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the number of rows deleted + */ + public int deleteBlobTrigger(Connection conn, String triggerName, + String groupName) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_BLOB_TRIGGER)); + ps.setString(1, triggerName); + ps.setString(2, groupName); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Delete the base trigger data for a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the number of rows deleted + */ + public int deleteTrigger(Connection conn, String triggerName, + String groupName) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_TRIGGER)); + ps.setString(1, triggerName); + ps.setString(2, groupName); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Select the number of triggers associated with a given job. + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the job + * @param groupName + * the group containing the job + * @return the number of triggers for the given job + */ + public int selectNumTriggersForJob(Connection conn, String jobName, + String groupName) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_NUM_TRIGGERS_FOR_JOB)); + ps.setString(1, jobName); + ps.setString(2, groupName); + rs = ps.executeQuery(); + + if (rs.next()) { + return rs.getInt(1); + } else { + return 0; + } + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Select the job to which the trigger is associated. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the{@link com.fr.third.org.quartz.JobDetail}
object
+ * associated with the given trigger
+ * @throws SQLException
+ * @throws ClassNotFoundException
+ */
+ public JobDetail selectJobForTrigger(Connection conn, String triggerName,
+ String groupName, ClassLoadHelper loadHelper) throws ClassNotFoundException, SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_JOB_FOR_TRIGGER));
+ ps.setString(1, triggerName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ JobDetail job = new JobDetail();
+ job.setName(rs.getString(1));
+ job.setGroup(rs.getString(2));
+ job.setDurability(getBoolean(rs, 3));
+ job.setJobClass(loadHelper.loadClass(rs
+ .getString(4)));
+ job.setRequestsRecovery(getBoolean(rs, 5));
+
+ return job;
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("No job for trigger '" + groupName + "."
+ + triggerName + "'.");
+ }
+ return null;
+ }
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Select the triggers for a job + *
+ * + * @param conn + * the DB Connection + * @param jobName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return an array of(@link com.fr.third.org.quartz.Trigger)
objects
+ * associated with a given job.
+ * @throws SQLException
+ */
+ public Trigger[] selectTriggersForJob(Connection conn, String jobName,
+ String groupName) throws SQLException, ClassNotFoundException,
+ IOException {
+
+ ArrayList trigList = new ArrayList();
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_FOR_JOB));
+ ps.setString(1, jobName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ while (rs.next()) {
+ Trigger t = selectTrigger(conn,
+ rs.getString(COL_TRIGGER_NAME),
+ rs.getString(COL_TRIGGER_GROUP));
+ if(t != null) {
+ trigList.add(t);
+ }
+ }
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+
+ return (Trigger[]) trigList.toArray(new Trigger[trigList.size()]);
+ }
+
+ public Trigger[] selectTriggersForCalendar(Connection conn, String calName)
+ throws SQLException, ClassNotFoundException, IOException {
+
+ ArrayList trigList = new ArrayList();
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_FOR_CALENDAR));
+ ps.setString(1, calName);
+ rs = ps.executeQuery();
+
+ while (rs.next()) {
+ trigList.add(selectTrigger(conn,
+ rs.getString(COL_TRIGGER_NAME), rs
+ .getString(COL_TRIGGER_GROUP)));
+ }
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+
+ return (Trigger[]) trigList.toArray(new Trigger[trigList.size()]);
+ }
+
+ public List selectStatefulJobsOfTriggerGroup(Connection conn,
+ String groupName) throws SQLException {
+ ArrayList jobList = new ArrayList();
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn
+ .prepareStatement(rtp(SELECT_STATEFUL_JOBS_OF_TRIGGER_GROUP));
+ ps.setString(1, groupName);
+ setBoolean(ps, 2, true);
+ rs = ps.executeQuery();
+
+ while (rs.next()) {
+ jobList.add(new Key(rs.getString(COL_JOB_NAME), rs
+ .getString(COL_JOB_GROUP)));
+ }
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+
+ return jobList;
+ }
+
+ /**
+ * + * Select a trigger. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the{@link com.fr.third.org.quartz.Trigger}
object
+ */
+ public Trigger selectTrigger(Connection conn, String triggerName,
+ String groupName) throws SQLException, ClassNotFoundException,
+ IOException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ Trigger trigger = null;
+
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGER));
+ ps.setString(1, triggerName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ String jobName = rs.getString(COL_JOB_NAME);
+ String jobGroup = rs.getString(COL_JOB_GROUP);
+ boolean volatility = getBoolean(rs, COL_IS_VOLATILE);
+ String description = rs.getString(COL_DESCRIPTION);
+ long nextFireTime = rs.getLong(COL_NEXT_FIRE_TIME);
+ long prevFireTime = rs.getLong(COL_PREV_FIRE_TIME);
+ String triggerType = rs.getString(COL_TRIGGER_TYPE);
+ long startTime = rs.getLong(COL_START_TIME);
+ long endTime = rs.getLong(COL_END_TIME);
+ String calendarName = rs.getString(COL_CALENDAR_NAME);
+ int misFireInstr = rs.getInt(COL_MISFIRE_INSTRUCTION);
+ int priority = rs.getInt(COL_PRIORITY);
+
+ Map map = null;
+ if (canUseProperties()) {
+ map = getMapFromProperties(rs);
+ } else {
+ map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP);
+ }
+
+ Date nft = null;
+ if (nextFireTime > 0) {
+ nft = new Date(nextFireTime);
+ }
+
+ Date pft = null;
+ if (prevFireTime > 0) {
+ pft = new Date(prevFireTime);
+ }
+ Date startTimeD = new Date(startTime);
+ Date endTimeD = null;
+ if (endTime > 0) {
+ endTimeD = new Date(endTime);
+ }
+
+ rs.close();
+ ps.close();
+
+ if (triggerType.equals(TTYPE_SIMPLE)) {
+ ps = conn.prepareStatement(rtp(SELECT_SIMPLE_TRIGGER));
+ ps.setString(1, triggerName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ int repeatCount = rs.getInt(COL_REPEAT_COUNT);
+ long repeatInterval = rs.getLong(COL_REPEAT_INTERVAL);
+ int timesTriggered = rs.getInt(COL_TIMES_TRIGGERED);
+
+ SimpleTrigger st = new SimpleTrigger(triggerName,
+ groupName, jobName, jobGroup, startTimeD,
+ endTimeD, repeatCount, repeatInterval);
+ st.setCalendarName(calendarName);
+ st.setMisfireInstruction(misFireInstr);
+ st.setTimesTriggered(timesTriggered);
+ st.setVolatility(volatility);
+ st.setNextFireTime(nft);
+ st.setPreviousFireTime(pft);
+ st.setDescription(description);
+ st.setPriority(priority);
+ if (null != map) {
+ st.setJobDataMap(new JobDataMap(map));
+ }
+ trigger = st;
+ }
+ } else if (triggerType.equals(TTYPE_CRON)) {
+ ps = conn.prepareStatement(rtp(SELECT_CRON_TRIGGER));
+ ps.setString(1, triggerName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ String cronExpr = rs.getString(COL_CRON_EXPRESSION);
+ String timeZoneId = rs.getString(COL_TIME_ZONE_ID);
+
+ CronTrigger ct = null;
+ try {
+ TimeZone timeZone = null;
+ if (timeZoneId != null) {
+ timeZone = TimeZone.getTimeZone(timeZoneId);
+ }
+ ct = new CronTrigger(triggerName, groupName,
+ jobName, jobGroup, startTimeD, endTimeD,
+ cronExpr, timeZone);
+ } catch (Exception neverHappens) {
+ // expr must be valid, or it never would have
+ // gotten to the store...
+ }
+ if (null != ct) {
+ ct.setCalendarName(calendarName);
+ ct.setMisfireInstruction(misFireInstr);
+ ct.setVolatility(volatility);
+ ct.setNextFireTime(nft);
+ ct.setPreviousFireTime(pft);
+ ct.setDescription(description);
+ ct.setPriority(priority);
+ if (null != map) {
+ ct.setJobDataMap(new JobDataMap(map));
+ }
+ trigger = ct;
+ }
+ }
+ } else if (triggerType.equals(TTYPE_BLOB)) {
+ ps = conn.prepareStatement(rtp(SELECT_BLOB_TRIGGER));
+ ps.setString(1, triggerName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ trigger = (Trigger) getObjectFromBlob(rs, COL_BLOB);
+ }
+ } else {
+ throw new ClassNotFoundException("class for trigger type '"
+ + triggerType + "' not found.");
+ }
+ }
+
+ return trigger;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Select a trigger's JobDataMap. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the{@link com.fr.third.org.quartz.JobDataMap}
of the Trigger,
+ * never null, but possibly empty.
+ */
+ public JobDataMap selectTriggerJobDataMap(Connection conn, String triggerName,
+ String groupName) throws SQLException, ClassNotFoundException,
+ IOException {
+
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ Trigger trigger = null;
+
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGER_DATA));
+ ps.setString(1, triggerName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+
+ Map map = null;
+ if (canUseProperties()) {
+ map = getMapFromProperties(rs);
+ } else {
+ map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP);
+ }
+
+ rs.close();
+ ps.close();
+
+ if (null != map) {
+ return new JobDataMap(map);
+ }
+ }
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+
+ return new JobDataMap();
+ }
+
+
+ /**
+ * + * Select a trigger' state value. + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return the{@link com.fr.third.org.quartz.Trigger}
object
+ */
+ public String selectTriggerState(Connection conn, String triggerName,
+ String groupName) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ String state = null;
+
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGER_STATE));
+ ps.setString(1, triggerName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ state = rs.getString(COL_TRIGGER_STATE);
+ } else {
+ state = STATE_DELETED;
+ }
+
+ return state.intern();
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+
+ }
+
+ /**
+ * + * Select a trigger' status (state & next fire time). + *
+ * + * @param conn + * the DB Connection + * @param triggerName + * the name of the trigger + * @param groupName + * the group containing the trigger + * @return aTriggerStatus
object, or null
+ */
+ public TriggerStatus selectTriggerStatus(Connection conn,
+ String triggerName, String groupName) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ TriggerStatus status = null;
+
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGER_STATUS));
+ ps.setString(1, triggerName);
+ ps.setString(2, groupName);
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ String state = rs.getString(COL_TRIGGER_STATE);
+ long nextFireTime = rs.getLong(COL_NEXT_FIRE_TIME);
+ String jobName = rs.getString(COL_JOB_NAME);
+ String jobGroup = rs.getString(COL_JOB_GROUP);
+
+ Date nft = null;
+ if (nextFireTime > 0) {
+ nft = new Date(nextFireTime);
+ }
+
+ status = new TriggerStatus(state, nft);
+ status.setKey(new Key(triggerName, groupName));
+ status.setJobKey(new Key(jobName, jobGroup));
+ }
+
+ return status;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+
+ }
+
+ /**
+ * + * Select the total number of triggers stored. + *
+ * + * @param conn + * the DB Connection + * @return the total number of triggers stored + */ + public int selectNumTriggers(Connection conn) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + int count = 0; + ps = conn.prepareStatement(rtp(SELECT_NUM_TRIGGERS)); + rs = ps.executeQuery(); + + if (rs.next()) { + count = rs.getInt(1); + } + + return count; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Select all of the trigger group names that are stored. + *
+ * + * @param conn + * the DB Connection + * @return an array ofString
group names
+ */
+ public String[] selectTriggerGroups(Connection conn) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGER_GROUPS));
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ list.add(rs.getString(1));
+ }
+
+ Object[] oArr = list.toArray();
+ String[] sArr = new String[oArr.length];
+ System.arraycopy(oArr, 0, sArr, 0, oArr.length);
+ return sArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Select all of the triggers contained in a given group. + *
+ * + * @param conn + * the DB Connection + * @param groupName + * the group containing the triggers + * @return an array ofString
trigger names
+ */
+ public String[] selectTriggersInGroup(Connection conn, String groupName)
+ throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_IN_GROUP));
+ ps.setString(1, groupName);
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ list.add(rs.getString(1));
+ }
+
+ Object[] oArr = list.toArray();
+ String[] sArr = new String[oArr.length];
+ System.arraycopy(oArr, 0, sArr, 0, oArr.length);
+ return sArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ public int insertPausedTriggerGroup(Connection conn, String groupName)
+ throws SQLException {
+ PreparedStatement ps = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(INSERT_PAUSED_TRIGGER_GROUP));
+ ps.setString(1, groupName);
+ int rows = ps.executeUpdate();
+
+ return rows;
+ } finally {
+ closeStatement(ps);
+ }
+ }
+
+ public int deletePausedTriggerGroup(Connection conn, String groupName)
+ throws SQLException {
+ PreparedStatement ps = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(DELETE_PAUSED_TRIGGER_GROUP));
+ ps.setString(1, groupName);
+ int rows = ps.executeUpdate();
+
+ return rows;
+ } finally {
+ closeStatement(ps);
+ }
+ }
+
+ public int deleteAllPausedTriggerGroups(Connection conn)
+ throws SQLException {
+ PreparedStatement ps = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(DELETE_PAUSED_TRIGGER_GROUPS));
+ int rows = ps.executeUpdate();
+
+ return rows;
+ } finally {
+ closeStatement(ps);
+ }
+ }
+
+ public boolean isTriggerGroupPaused(Connection conn, String groupName)
+ throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_PAUSED_TRIGGER_GROUP));
+ ps.setString(1, groupName);
+ rs = ps.executeQuery();
+
+ return rs.next();
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ public boolean isExistingTriggerGroup(Connection conn, String groupName)
+ throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_NUM_TRIGGERS_IN_GROUP));
+ ps.setString(1, groupName);
+ rs = ps.executeQuery();
+
+ if (!rs.next()) {
+ return false;
+ }
+
+ return (rs.getInt(1) > 0);
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ //---------------------------------------------------------------------------
+ // calendars
+ //---------------------------------------------------------------------------
+
+ /**
+ * + * Insert a new calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name for the new calendar + * @param calendar + * the calendar + * @return the number of rows inserted + * @throws IOException + * if there were problems serializing the calendar + */ + public int insertCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException { + ByteArrayOutputStream baos = serializeObject(calendar); + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(INSERT_CALENDAR)); + ps.setString(1, calendarName); + setBytes(ps, 2, baos); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Update a calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name for the new calendar + * @param calendar + * the calendar + * @return the number of rows updated + * @throws IOException + * if there were problems serializing the calendar + */ + public int updateCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException { + ByteArrayOutputStream baos = serializeObject(calendar); + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_CALENDAR)); + setBytes(ps, 1, baos); + ps.setString(2, calendarName); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Check whether or not a calendar exists. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name of the calendar + * @return true if the trigger exists, false otherwise + */ + public boolean calendarExists(Connection conn, String calendarName) + throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_CALENDAR_EXISTENCE)); + ps.setString(1, calendarName); + rs = ps.executeQuery(); + + if (rs.next()) { + return true; + } else { + return false; + } + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Select a calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name of the calendar + * @return the Calendar + * @throws ClassNotFoundException + * if a class found during deserialization cannot be found be + * found + * @throws IOException + * if there were problems deserializing the calendar + */ + public Calendar selectCalendar(Connection conn, String calendarName) + throws ClassNotFoundException, IOException, SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String selCal = rtp(SELECT_CALENDAR); + ps = conn.prepareStatement(selCal); + ps.setString(1, calendarName); + rs = ps.executeQuery(); + + Calendar cal = null; + if (rs.next()) { + cal = (Calendar) getObjectFromBlob(rs, COL_CALENDAR); + } + if (null == cal) { + logger.warn("Couldn't find calendar with name '" + calendarName + + "'."); + } + return cal; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Check whether or not a calendar is referenced by any triggers. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name of the calendar + * @return true if any triggers reference the calendar, false otherwise + */ + public boolean calendarIsReferenced(Connection conn, String calendarName) + throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = conn.prepareStatement(rtp(SELECT_REFERENCED_CALENDAR)); + ps.setString(1, calendarName); + rs = ps.executeQuery(); + + if (rs.next()) { + return true; + } else { + return false; + } + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Delete a calendar. + *
+ * + * @param conn + * the DB Connection + * @param calendarName + * the name of the trigger + * @return the number of rows deleted + */ + public int deleteCalendar(Connection conn, String calendarName) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_CALENDAR)); + ps.setString(1, calendarName); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *+ * Select the total number of calendars stored. + *
+ * + * @param conn + * the DB Connection + * @return the total number of calendars stored + */ + public int selectNumCalendars(Connection conn) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + int count = 0; + ps = conn.prepareStatement(rtp(SELECT_NUM_CALENDARS)); + + rs = ps.executeQuery(); + + if (rs.next()) { + count = rs.getInt(1); + } + + return count; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Select all of the stored calendars. + *
+ * + * @param conn + * the DB Connection + * @return an array ofString
calendar names
+ */
+ public String[] selectCalendars(Connection conn) throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_CALENDARS));
+ rs = ps.executeQuery();
+
+ ArrayList list = new ArrayList();
+ while (rs.next()) {
+ list.add(rs.getString(1));
+ }
+
+ Object[] oArr = list.toArray();
+ String[] sArr = new String[oArr.length];
+ System.arraycopy(oArr, 0, sArr, 0, oArr.length);
+ return sArr;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ //---------------------------------------------------------------------------
+ // trigger firing
+ //---------------------------------------------------------------------------
+
+ /**
+ * + * Select the next time that a trigger will be fired. + *
+ * + * @param conn + * the DB Connection + * @return the next fire time, or 0 if no trigger will be fired + * + * @deprecated Does not account for misfires. + */ + public long selectNextFireTime(Connection conn) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = conn.prepareStatement(rtp(SELECT_NEXT_FIRE_TIME)); + ps.setString(1, STATE_WAITING); + rs = ps.executeQuery(); + + if (rs.next()) { + return rs.getLong(ALIAS_COL_NEXT_FIRE_TIME); + } else { + return 0l; + } + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Select the trigger that will be fired at the given fire time. + *
+ * + * @param conn + * the DB Connection + * @param fireTime + * the time that the trigger will be fired + * @return a{@link com.fr.third.org.quartz.utils.Key}
representing the
+ * trigger that will be fired at the given fire time, or null if no
+ * trigger will be fired at that time
+ */
+ public Key selectTriggerForFireTime(Connection conn, long fireTime)
+ throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_TRIGGER_FOR_FIRE_TIME));
+ ps.setString(1, STATE_WAITING);
+ ps.setBigDecimal(2, new BigDecimal(String.valueOf(fireTime)));
+ rs = ps.executeQuery();
+
+ if (rs.next()) {
+ return new Key(rs.getString(COL_TRIGGER_NAME), rs
+ .getString(COL_TRIGGER_GROUP));
+ } else {
+ return null;
+ }
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Select the next trigger which will fire to fire between the two given timestamps + * in ascending order of fire time, and then descending by priority. + *
+ * + * @param conn + * the DB Connection + * @param noLaterThan + * highest value ofgetNextFireTime()
of the triggers (exclusive)
+ * @param noEarlierThan
+ * highest value of getNextFireTime()
of the triggers (inclusive)
+ *
+ * @return A (never null, possibly empty) list of the identifiers (Key objects) of the next triggers to be fired.
+ */
+ public List selectTriggerToAcquire(Connection conn, long noLaterThan, long noEarlierThan)
+ throws SQLException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ List nextTriggers = new LinkedList();
+ try {
+ ps = conn.prepareStatement(rtp(SELECT_NEXT_TRIGGER_TO_ACQUIRE));
+
+ // Try to give jdbc driver a hint to hopefully not pull over
+ // more than the few rows we actually need.
+ ps.setFetchSize(5);
+ ps.setMaxRows(5);
+
+ ps.setString(1, STATE_WAITING);
+ ps.setBigDecimal(2, new BigDecimal(String.valueOf(noLaterThan)));
+ ps.setBigDecimal(3, new BigDecimal(String.valueOf(noEarlierThan)));
+ rs = ps.executeQuery();
+
+ while (rs.next() && nextTriggers.size() < 5) {
+ nextTriggers.add(new Key(
+ rs.getString(COL_TRIGGER_NAME),
+ rs.getString(COL_TRIGGER_GROUP)));
+ }
+
+ return nextTriggers;
+ } finally {
+ closeResultSet(rs);
+ closeStatement(ps);
+ }
+ }
+
+ /**
+ * + * Insert a fired trigger. + *
+ * + * @param conn + * the DB Connection + * @param trigger + * the trigger + * @param state + * the state that the trigger should be stored in + * @return the number of rows inserted + */ + public int insertFiredTrigger(Connection conn, Trigger trigger, + String state, JobDetail job) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(rtp(INSERT_FIRED_TRIGGER)); + ps.setString(1, trigger.getFireInstanceId()); + ps.setString(2, trigger.getName()); + ps.setString(3, trigger.getGroup()); + setBoolean(ps, 4, trigger.isVolatile()); + ps.setString(5, instanceId); + ps.setBigDecimal(6, new BigDecimal(String.valueOf(trigger + .getNextFireTime().getTime()))); + ps.setString(7, state); + if (job != null) { + ps.setString(8, trigger.getJobName()); + ps.setString(9, trigger.getJobGroup()); + setBoolean(ps, 10, job.isStateful()); + setBoolean(ps, 11, job.requestsRecovery()); + } else { + ps.setString(8, null); + ps.setString(9, null); + setBoolean(ps, 10, false); + setBoolean(ps, 11, false); + } + ps.setInt(12, trigger.getPriority()); + + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + /** + *
+ * Select the states of all fired-trigger records for a given trigger, or
+ * trigger group if trigger name is null
.
+ *
+ * Select the states of all fired-trigger records for a given job, or job
+ * group if job name is null
.
+ *
+ * Select the distinct instance names of all fired-trigger records. + *
+ * + *+ * This is useful when trying to identify orphaned fired triggers (a + * fired trigger without a scheduler state record.) + *
+ * + * @return a Set of String objects. + */ + public Set selectFiredTriggerInstanceNames(Connection conn) + throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + Set instanceNames = new HashSet(); + + ps = conn.prepareStatement(rtp(SELECT_FIRED_TRIGGER_INSTANCE_NAMES)); + rs = ps.executeQuery(); + + while (rs.next()) { + instanceNames.add(rs.getString(COL_INSTANCE_NAME)); + } + + return instanceNames; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *+ * Delete a fired trigger. + *
+ * + * @param conn + * the DB Connection + * @param entryId + * the fired trigger entry to delete + * @return the number of rows deleted + */ + public int deleteFiredTrigger(Connection conn, String entryId) + throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(rtp(DELETE_FIRED_TRIGGER)); + ps.setString(1, entryId); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + public int selectJobExecutionCount(Connection conn, String jobName, + String jobGroup) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_JOB_EXECUTION_COUNT)); + ps.setString(1, jobName); + ps.setString(2, jobGroup); + + rs = ps.executeQuery(); + + return (rs.next()) ? rs.getInt(1) : 0; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + public int deleteVolatileFiredTriggers(Connection conn) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(rtp(DELETE_VOLATILE_FIRED_TRIGGERS)); + setBoolean(ps, 1, true); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + public int insertSchedulerState(Connection conn, String instanceId, + long checkInTime, long interval) + throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(rtp(INSERT_SCHEDULER_STATE)); + ps.setString(1, instanceId); + ps.setLong(2, checkInTime); + ps.setLong(3, interval); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + public int deleteSchedulerState(Connection conn, String instanceId) + throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(rtp(DELETE_SCHEDULER_STATE)); + ps.setString(1, instanceId); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + public int updateSchedulerState(Connection conn, String instanceId, long checkInTime) + throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(rtp(UPDATE_SCHEDULER_STATE)); + ps.setLong(1, checkInTime); + ps.setString(2, instanceId); + + return ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + public List selectSchedulerStateRecords(Connection conn, String instanceId) + throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + List lst = new LinkedList(); + + if (instanceId != null) { + ps = conn.prepareStatement(rtp(SELECT_SCHEDULER_STATE)); + ps.setString(1, instanceId); + } else { + ps = conn.prepareStatement(rtp(SELECT_SCHEDULER_STATES)); + } + rs = ps.executeQuery(); + + while (rs.next()) { + SchedulerStateRecord rec = new SchedulerStateRecord(); + + rec.setSchedulerInstanceId(rs.getString(COL_INSTANCE_NAME)); + rec.setCheckinTimestamp(rs.getLong(COL_LAST_CHECKIN_TIME)); + rec.setCheckinInterval(rs.getLong(COL_CHECKIN_INTERVAL)); + + lst.add(rec); + } + + return lst; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + + } + + //--------------------------------------------------------------------------- + // protected methods that can be overridden by subclasses + //--------------------------------------------------------------------------- + + /** + *+ * Replace the table prefix in a query by replacing any occurrences of + * "{0}" with the table prefix. + *
+ * + * @param query + * the unsubstitued query + * @return the query, with proper table prefix substituted + */ + protected final String rtp(String query) { + return Util.rtp(query, tablePrefix); + } + + /** + *
+ * Create a serialized java.util.ByteArrayOutputStream
+ * version of an Object.
+ *
+ * Remove the transient data from and then create a serialized java.util.ByteArrayOutputStream
+ * version of a {@link com.fr.third.org.quartz.JobDataMap}
.
+ *
+ * This method should be overridden by any delegate subclasses that need
+ * special handling for BLOBs. The default implementation uses standard
+ * JDBC java.sql.Blob
operations.
+ *
+ * This method should be overridden by any delegate subclasses that need
+ * special handling for BLOBs for job details. The default implementation
+ * uses standard JDBC java.sql.Blob
operations.
+ *
ResultSet
+ * while ignoring any errors.
+ */
+ protected void closeResultSet(ResultSet rs) {
+ if (null != rs) {
+ try {
+ rs.close();
+ } catch (SQLException ignore) {
+ }
+ }
+ }
+
+ /**
+ * Cleanup helper method that closes the given Statement
+ * while ignoring any errors.
+ */
+ protected void closeStatement(Statement statement) {
+ if (null != statement) {
+ try {
+ statement.close();
+ } catch (SQLException ignore) {
+ }
+ }
+ }
+
+
+ /**
+ * Sets the designated parameter to the given Java boolean
value.
+ * This just wraps {@link PreparedStatement#setBoolean(int, boolean)}
+ * by default, but it can be overloaded by subclass delegates for databases that
+ * don't explicitly support the boolean type.
+ */
+ protected void setBoolean(PreparedStatement ps, int index, boolean val) throws SQLException {
+ ps.setBoolean(index, val);
+ }
+
+ /**
+ * Retrieves the value of the designated column in the current row as
+ * a boolean
.
+ * This just wraps {@link ResultSet#getBoolean(java.lang.String)}
+ * by default, but it can be overloaded by subclass delegates for databases that
+ * don't explicitly support the boolean type.
+ */
+ protected boolean getBoolean(ResultSet rs, String columnName) throws SQLException {
+ return rs.getBoolean(columnName);
+ }
+
+ /**
+ * Retrieves the value of the designated column index in the current row as
+ * a boolean
.
+ * This just wraps {@link ResultSet#getBoolean(java.lang.String)}
+ * by default, but it can be overloaded by subclass delegates for databases that
+ * don't explicitly support the boolean type.
+ */
+ protected boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException {
+ return rs.getBoolean(columnIndex);
+ }
+
+ /**
+ * Sets the designated parameter to the byte array of the given
+ * ByteArrayOutputStream
. Will set parameter value to null if the
+ * ByteArrayOutputStream
is null.
+ * This just wraps {@link PreparedStatement#setBytes(int, byte[])}
+ * by default, but it can be overloaded by subclass delegates for databases that
+ * don't explicitly support storing bytes in this way.
+ */
+ protected void setBytes(PreparedStatement ps, int index, ByteArrayOutputStream baos) throws SQLException {
+ ps.setBytes(index, (baos == null) ? new byte[0] : baos.toByteArray());
+ }
+}
+
+// EOF
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/StdRowLockSemaphore.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/StdRowLockSemaphore.java
new file mode 100644
index 000000000..bb29f8b3d
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/StdRowLockSemaphore.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * Internal database based lock handler for providing thread/resource locking
+ * in order to protect resources from being altered by multiple threads at the
+ * same time.
+ *
+ * @author jhouse
+ */
+public class StdRowLockSemaphore extends DBSemaphore {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constants.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public static final String SELECT_FOR_LOCK = "SELECT * FROM "
+ + TABLE_PREFIX_SUBST + TABLE_LOCKS + " WHERE " + COL_LOCK_NAME
+ + " = ? FOR UPDATE";
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * This constructor is for using the StdRowLockSemaphore
as
+ * a bean.
+ */
+ public StdRowLockSemaphore() {
+ super(DEFAULT_TABLE_PREFIX, null, SELECT_FOR_LOCK);
+ }
+
+ public StdRowLockSemaphore(String tablePrefix, String selectWithLockSQL) {
+ super(tablePrefix, selectWithLockSQL, SELECT_FOR_LOCK);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Execute the SQL select for update that will lock the proper database row.
+ */
+ protected void executeSQL(Connection conn, String lockName, String expandedSQL) throws LockException {
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ try {
+ ps = conn.prepareStatement(expandedSQL);
+ ps.setString(1, lockName);
+
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(
+ "Lock '" + lockName + "' is being obtained: " +
+ Thread.currentThread().getName());
+ }
+ rs = ps.executeQuery();
+ if (!rs.next()) {
+ throw new SQLException(Util.rtp(
+ "No row exists in table " + TABLE_PREFIX_SUBST +
+ TABLE_LOCKS + " for lock named: " + lockName, getTablePrefix()));
+ }
+ } catch (SQLException sqle) {
+ //Exception src =
+ // (Exception)getThreadLocksObtainer().get(lockName);
+ //if(src != null)
+ // src.printStackTrace();
+ //else
+ // System.err.println("--- ***************** NO OBTAINER!");
+
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(
+ "Lock '" + lockName + "' was not obtained by: " +
+ Thread.currentThread().getName());
+ }
+
+ throw new LockException("Failure obtaining db row lock: "
+ + sqle.getMessage(), sqle);
+ } finally {
+ if (rs != null) {
+ try {
+ rs.close();
+ } catch (Exception ignore) {
+ }
+ }
+ if (ps != null) {
+ try {
+ ps.close();
+ } catch (Exception ignore) {
+ }
+ }
+ }
+ }
+
+ protected String getSelectWithLockSQL() {
+ return getSQL();
+ }
+
+ public void setSelectWithLockSQL(String selectWithLockSQL) {
+ setSQL(selectWithLockSQL);
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/TablePrefixAware.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/TablePrefixAware.java
new file mode 100644
index 000000000..297c41a0b
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/TablePrefixAware.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2004-2006 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+/**
+ * Interface for Quartz objects that need to know what the table prefix of
+ * the tables used by a JDBC JobStore is.
+ */
+public interface TablePrefixAware {
+ void setTablePrefix(String tablePrefix);
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/UpdateLockRowSemaphore.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/UpdateLockRowSemaphore.java
new file mode 100644
index 000000000..5f7406d09
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/UpdateLockRowSemaphore.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2004-2006 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.fr.third.org.quartz.impl.jdbcjobstore;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Provide thread/resource locking in order to protect
+ * resources from being altered by multiple threads at the same time using
+ * a db row update.
+ *
+ * + * Note: This Semaphore implementation is useful for databases that do + * not support row locking via "SELECT FOR UPDATE" type syntax, for example + * Microsoft SQLServer (MSSQL). + *
+ */ +public class UpdateLockRowSemaphore extends DBSemaphore { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public static final String UPDATE_FOR_LOCK = + "UPDATE " + TABLE_PREFIX_SUBST + TABLE_LOCKS + + " SET " + COL_LOCK_NAME + " = " + COL_LOCK_NAME + + " WHERE " + COL_LOCK_NAME + " = ? "; + + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public UpdateLockRowSemaphore() { + super(DEFAULT_TABLE_PREFIX, null, UPDATE_FOR_LOCK); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Execute the SQL select for update that will lock the proper database row. + */ + protected void executeSQL(Connection conn, String lockName, String expandedSQL) throws LockException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(expandedSQL); + ps.setString(1, lockName); + + if (getLog().isDebugEnabled()) { + getLog().debug( + "Lock '" + lockName + "' is being obtained: " + + Thread.currentThread().getName()); + } + + int numUpdate = ps.executeUpdate(); + + if (numUpdate < 1) { + throw new SQLException(Util.rtp( + "No row exists in table " + TABLE_PREFIX_SUBST + TABLE_LOCKS + + " for lock named: " + lockName, getTablePrefix())); + } + } catch (SQLException sqle) { + //Exception src = + // (Exception)getThreadLocksObtainer().get(lockName); + //if(src != null) + // src.printStackTrace(); + //else + // System.err.println("--- ***************** NO OBTAINER!"); + + if(getLog().isDebugEnabled()) { + getLog().debug( + "Lock '" + lockName + "' was not obtained by: " + + Thread.currentThread().getName()); + } + + throw new LockException( + "Failure obtaining db row lock: " + sqle.getMessage(), sqle); + } finally { + if (ps != null) { + try { + ps.close(); + } catch (Exception ignore) { + } + } + } + } + + protected String getUpdateLockRowSQL() { + return getSQL(); + } + + public void setUpdateLockRowSQL(String updateLockRowSQL) { + setSQL(updateLockRowSQL); + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/Util.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/Util.java new file mode 100644 index 000000000..8891f0b64 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/jdbcjobstore/Util.java @@ -0,0 +1,95 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.impl.jdbcjobstore; + +import java.text.MessageFormat; + +/** + *+ * This class contains utility functions for use in all delegate classes. + *
+ * + * @author Jeffrey Wescott + */ +public final class Util { + + /** + * Private constructor because this is a pure utility class. + */ + private Util() { + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *+ * Replace the table prefix in a query by replacing any occurrences of + * "{0}" with the table prefix. + *
+ * + * @param query + * the unsubstitued query + * @param tablePrefix + * the table prefix + * @return the query, with proper table prefix substituted + */ + public static String rtp(String query, String tablePrefix) { + return MessageFormat.format(query, new Object[]{tablePrefix}); + } + + /** + *+ * Obtain a unique key for a given job. + *
+ * + * @param jobName + * the job name + * @param groupName + * the group containing the job + * @return a uniqueString
key
+ */
+ static String getJobNameKey(String jobName, String groupName) {
+ return (groupName + "_$x$x$_" + jobName).intern();
+ }
+
+ /**
+ * + * Obtain a unique key for a given trigger. + *
+ * + * @param triggerName + * the trigger name + * @param groupName + * the group containing the trigger + * @return a uniqueString
key
+ */
+ static String getTriggerNameKey(String triggerName, String groupName) {
+ return (groupName + "_$x$x$_" + triggerName).intern();
+ }
+}
+
+// EOF
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/package.html b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/package.html
new file mode 100644
index 000000000..adb720946
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/impl/package.html
@@ -0,0 +1,18 @@
+
+
+Contains implementations of the SchedulerFactory, JobStore, ThreadPool, and +other interfaces required by the com.fr.third.org.quartz.core.QuartzScheduler.
+ +Classes in this package may have dependencies on third-party packages.
+ +FileScanListener
that can be found in the
+ * SchedulerContext
.
+ *
+ * @author jhouse
+ * @author pl47ypus
+ * @see com.fr.third.org.quartz.jobs.FileScanListener
+ */
+public class FileScanJob implements StatefulJob {
+
+ public static final String FILE_NAME = "FILE_NAME";
+ public static final String FILE_SCAN_LISTENER_NAME = "FILE_SCAN_LISTENER_NAME";
+ private static final String LAST_MODIFIED_TIME = "LAST_MODIFIED_TIME";
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ public FileScanJob() {
+ }
+
+ /**
+ * @see com.fr.third.org.quartz.Job#execute(com.fr.third.org.quartz.JobExecutionContext)
+ */
+ public void execute(JobExecutionContext context) throws JobExecutionException {
+ JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
+ SchedulerContext schedCtxt = null;
+ try {
+ schedCtxt = context.getScheduler().getContext();
+ } catch (SchedulerException e) {
+ throw new JobExecutionException("Error obtaining scheduler context.", e, false);
+ }
+
+ String fileName = mergedJobDataMap.getString(FILE_NAME);
+ String listenerName = mergedJobDataMap.getString(FILE_SCAN_LISTENER_NAME);
+
+ if(fileName == null) {
+ throw new JobExecutionException("Required parameter '" +
+ FILE_NAME + "' not found in merged JobDataMap");
+ }
+ if(listenerName == null) {
+ throw new JobExecutionException("Required parameter '" +
+ FILE_SCAN_LISTENER_NAME + "' not found in merged JobDataMap");
+ }
+
+ FileScanListener listener = (FileScanListener)schedCtxt.get(listenerName);
+
+ if(listener == null) {
+ throw new JobExecutionException("FileScanListener named '" +
+ listenerName + "' not found in SchedulerContext");
+ }
+
+ long lastDate = -1;
+ if(mergedJobDataMap.containsKey(LAST_MODIFIED_TIME)) {
+ lastDate = mergedJobDataMap.getLong(LAST_MODIFIED_TIME);
+ }
+
+ long newDate = getLastModifiedDate(fileName);
+
+ if(newDate < 0) {
+ log.warn("File '"+fileName+"' does not exist.");
+ return;
+ }
+
+ if(lastDate > 0 && (newDate != lastDate)) {
+ // notify call back...
+ log.info("File '"+fileName+"' updated, notifying listener.");
+ listener.fileUpdated(fileName);
+ } else if (log.isDebugEnabled()) {
+ log.debug("File '"+fileName+"' unchanged.");
+ }
+
+ // It is the JobDataMap on the JobDetail which is actually stateful
+ context.getJobDetail().getJobDataMap().put(LAST_MODIFIED_TIME, newDate);
+ }
+
+ protected long getLastModifiedDate(String fileName) {
+ URL resource = Thread.currentThread().getContextClassLoader().getResource(fileName);
+
+ // Get the absolute path.
+ String filePath = (resource == null) ? fileName : URLDecoder.decode(resource.getFile()); ;
+
+ // If the jobs file is inside a jar point to the jar file (to get it modification date).
+ // Otherwise continue as usual.
+ int jarIndicator = filePath.indexOf('!');
+
+ if (jarIndicator > 0) {
+ filePath = filePath.substring(5, filePath.indexOf('!'));
+ }
+
+ File file = new File(filePath);
+
+ if(!file.exists()) {
+ return -1;
+ } else {
+ return file.lastModified();
+ }
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/FileScanJob.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/FileScanJob.java.bak
new file mode 100644
index 000000000..260f737c9
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/FileScanJob.java.bak
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.jobs;
+
+import java.io.File;
+
+import org.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.SchedulerContext;
+import org.quartz.SchedulerException;
+import org.quartz.StatefulJob;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Inspects a file and compares whether it's "last modified date" has changed
+ * since the last time it was inspected. If the file has been updated, the
+ * job invokes a "call-back" method on an identified
+ * FileScanListener
that can be found in the
+ * SchedulerContext
.
+ *
+ * @author jhouse
+ * @see org.quartz.jobs.FileScanListener
+ */
+public class FileScanJob implements StatefulJob {
+
+ public static String FILE_NAME = "FILE_NAME";
+ public static String FILE_SCAN_LISTENER_NAME = "FILE_SCAN_LISTENER_NAME";
+ private static String LAST_MODIFIED_TIME = "LAST_MODIFIED_TIME";
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ public FileScanJob() {
+ }
+
+ /**
+ * @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
+ */
+ public void execute(JobExecutionContext context) throws JobExecutionException {
+ JobDataMap mergedJobDataMap = context.getMergedJobDataMap();
+ SchedulerContext schedCtxt = null;
+ try {
+ schedCtxt = context.getScheduler().getContext();
+ } catch (SchedulerException e) {
+ throw new JobExecutionException("Error obtaining scheduler context.", e, false);
+ }
+
+ String fileName = mergedJobDataMap.getString(FILE_NAME);
+ String listenerName = mergedJobDataMap.getString(FILE_SCAN_LISTENER_NAME);
+
+ if(fileName == null) {
+ throw new JobExecutionException("Required parameter '" +
+ FILE_NAME + "' not found in merged JobDataMap");
+ }
+ if(listenerName == null) {
+ throw new JobExecutionException("Required parameter '" +
+ FILE_SCAN_LISTENER_NAME + "' not found in merged JobDataMap");
+ }
+
+ FileScanListener listener = (FileScanListener)schedCtxt.get(listenerName);
+
+ if(listener == null) {
+ throw new JobExecutionException("FileScanListener named '" +
+ listenerName + "' not found in SchedulerContext");
+ }
+
+ long lastDate = -1;
+ if(mergedJobDataMap.containsKey(LAST_MODIFIED_TIME)) {
+ lastDate = mergedJobDataMap.getLong(LAST_MODIFIED_TIME);
+ }
+
+ long newDate = getLastModifiedDate(fileName);
+
+ if(newDate < 0) {
+ log.warn("File '"+fileName+"' does not exist.");
+ return;
+ }
+
+ if(lastDate > 0 && (newDate != lastDate)) {
+ // notify call back...
+ log.info("File '"+fileName+"' updated, notifying listener.");
+ listener.fileUpdated(fileName);
+ } else if (log.isDebugEnabled()) {
+ log.debug("File '"+fileName+"' unchanged.");
+ }
+
+ // It is the JobDataMap on the JobDetail which is actually stateful
+ context.getJobDetail().getJobDataMap().put(LAST_MODIFIED_TIME, newDate);
+ }
+
+ protected long getLastModifiedDate(String fileName) {
+
+ File file = new File(fileName);
+
+ if(!file.exists()) {
+ return -1;
+ } else {
+ return file.lastModified();
+ }
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/FileScanListener.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/FileScanListener.java
new file mode 100644
index 000000000..e2c1674b2
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/FileScanListener.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.jobs;
+
+/**
+ * Interface for objects wishing to receive a 'call-back' from a
+ * FileScanJob
.
+ *
+ * @author jhouse
+ * @see com.fr.third.org.quartz.jobs.FileScanJob
+ */
+public interface FileScanListener {
+
+ void fileUpdated(String fileName);
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/NativeJob.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/NativeJob.java
new file mode 100644
index 000000000..ee42fe012
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/NativeJob.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+
+package com.fr.third.org.quartz.jobs;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.Job;
+import com.fr.third.org.quartz.JobDataMap;
+import com.fr.third.org.quartz.JobExecutionContext;
+import com.fr.third.org.quartz.JobExecutionException;
+
+/*
+ * Built in job for executing native executables in a separate process.
+ * + * If PROP_WAIT_FOR_PROCESS is true, then the Integer exit value of the process + * will be saved as the job execution result in the JobExecutionContext. + * + * @see #PROP_COMMAND + * @see #PROP_PARAMETERS + * @see #PROP_WAIT_FOR_PROCESS + * @see #PROP_CONSUME_STREAMS + * + * @author Matthew Payne + * @author James House + * @author Steinar Overbeck Cook + * @date Sep 17, 2003 @Time: 11:27:13 AM + */ +public class NativeJob implements Job { + + private final Log log = LogFactory.getLog(getClass()); + + /* + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Required parameter that specifies the name of the command (executable) + * to be ran. + */ + public static final String PROP_COMMAND = "command"; + + /** + * Optional parameter that specifies the parameters to be passed to the + * executed command. + */ + public static final String PROP_PARAMETERS = "parameters"; + + + /** + * Optional parameter (value should be 'true' or 'false') that specifies + * whether the job should wait for the execution of the native process to + * complete before it completes. + * + *Defaults to true
.
Defaults to false
.
+ * An implementation of Job, that does absolutely nothing - useful for system
+ * which only wish to use {@link com.fr.third.org.quartz.TriggerListener}s
+ * and {@link com.fr.third.org.quartz.JobListener}s
, rather than writing
+ * Jobs that perform work.
+ *
+ * Do nothing. + *
+ */ + public void execute(JobExecutionContext context) + throws JobExecutionException { + } + +} \ No newline at end of file diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/ee/jmx/JMXInvokerJob.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/ee/jmx/JMXInvokerJob.java new file mode 100644 index 000000000..c21f1d066 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/jobs/ee/jmx/JMXInvokerJob.java @@ -0,0 +1,190 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.jobs.ee.jmx; + + +import java.util.LinkedList; +import java.util.StringTokenizer; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import com.fr.third.org.quartz.Job; +import com.fr.third.org.quartz.JobDataMap; +import com.fr.third.org.quartz.JobExecutionContext; +import com.fr.third.org.quartz.JobExecutionException; + + +/** + * Generic JMX invoker Job. It supports any number or type of parameters + * to the JMX bean.+ * + * The required parameters are as follows (case doesn't matter):
+ *
+ * i - is for int
+ * l - is for long
+ * f - is for float
+ * d - is for double
+ * s - is for String
+ * b - is for boolean
+ * For ilfdb use lower for native type and upper for object wrapper. The name portion
+ * of the definition is the name of the parameter holding the string value. (ie
+ * s:fname,s:lname would require 2 parameters of the name fname and lname and
+ * would be passed in that order to the method.
+ *
+ * @author James Nelson (jmn@provident-solutions.com) -- Provident Solutions LLC
+ *
+ */
+public class JMXInvokerJob implements Job {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ public void execute(JobExecutionContext context) throws JobExecutionException {
+ try {
+ Object[] params=null;
+ String[] types=null;
+ String objName = null;
+ String objMethod = null;
+
+ JobDataMap jobDataMap = context.getMergedJobDataMap();
+
+ String[] keys = jobDataMap.getKeys();
+ for (int i = 0; i < keys.length; i++) {
+ String value = jobDataMap.getString(keys[i]);
+ if ("JMX_OBJECTNAME".equalsIgnoreCase(keys[i])) {
+ objName = value;
+ } else if ("JMX_METHOD".equalsIgnoreCase(keys[i])) {
+ objMethod = value;
+ } else if("JMX_PARAMDEFS".equalsIgnoreCase(keys[i])) {
+ String[] paramdefs=split(value, ",");
+ params=new Object[paramdefs.length];
+ types=new String[paramdefs.length];
+ for(int k=0;k
This may be more convenient than registering all of the listeners + * directly with the Scheduler, and provides the flexibility of easily changing + * which listeners get notified.
+ * + * @see #addListener(com.fr.third.org.quartz.SchedulerListener) + * @see #removeListener(com.fr.third.org.quartz.SchedulerListener) + * + * @author James House (jhouse AT revolition DOT net) + */ +public class BroadcastSchedulerListener implements SchedulerListener { + + private List listeners; + + public BroadcastSchedulerListener() { + listeners = new LinkedList(); + } + + /** + * Construct an instance with the given List of listeners. + * + * @param listeners the initial List of SchedulerListeners to broadcast to. + */ + public BroadcastSchedulerListener(List listeners) { + this(); + this.listeners.addAll(listeners); + } + + + public void addListener(SchedulerListener listener) { + listeners.add(listener); + } + + public boolean removeListener(SchedulerListener listener) { + return listeners.remove(listener); + } + + public List getListeners() { + return java.util.Collections.unmodifiableList(listeners); + } + + public void jobScheduled(Trigger trigger) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = (SchedulerListener) itr.next(); + l.jobScheduled(trigger); + } + } + + public void jobUnscheduled(String triggerName, String triggerGroup) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = (SchedulerListener) itr.next(); + l.jobUnscheduled(triggerName, triggerGroup); + } + } + + public void triggerFinalized(Trigger trigger) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = (SchedulerListener) itr.next(); + l.triggerFinalized(trigger); + } + } + + public void triggersPaused(String triggerName, String triggerGroup) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = (SchedulerListener) itr.next(); + l.triggersPaused(triggerName, triggerGroup); + } + } + + public void triggersResumed(String triggerName, String triggerGroup) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = (SchedulerListener) itr.next(); + l.triggersResumed(triggerName, triggerGroup); + } + } + + public void jobsPaused(String jobName, String jobGroup) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = (SchedulerListener) itr.next(); + l.jobsPaused(jobName, jobGroup); + } + } + + public void jobsResumed(String jobName, String jobGroup) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = (SchedulerListener) itr.next(); + l.jobsResumed(jobName, jobGroup); + } + } + + public void schedulerError(String msg, SchedulerException cause) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = (SchedulerListener) itr.next(); + l.schedulerError(msg, cause); + } + } + + public void schedulerShutdown() { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = (SchedulerListener) itr.next(); + l.schedulerShutdown(); + } + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/FilterAndBroadcastJobListener.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/FilterAndBroadcastJobListener.java new file mode 100644 index 000000000..1bb396883 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/FilterAndBroadcastJobListener.java @@ -0,0 +1,214 @@ +/* + * Copyright 2004-2006 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.fr.third.org.quartz.listeners; + +import java.util.LinkedList; +import java.util.List; +import java.util.Iterator; + +import com.fr.third.org.quartz.JobListener; +import com.fr.third.org.quartz.JobExecutionContext; +import com.fr.third.org.quartz.JobExecutionException; +import com.fr.third.org.quartz.JobDetail; + +/** + * Holds a List of references to JobListener instances and broadcasts all + * events to them (in order) - if the event is not excluded via filtering + * (read on). + * + *The broadcasting behavior of this listener to delegate listeners may be + * more convenient than registering all of the listeners directly with the + * Trigger, and provides the flexibility of easily changing which listeners + * get notified.
+ * + *You may also register a number of Regular Expression patterns to match + * the events against. If one or more patterns are registered, the broadcast + * will only take place if the event applies to a job who's name/group + * matches one or more of the patterns.
+ * + * @see #addListener(com.fr.third.org.quartz.JobListener) + * @see #removeListener(com.fr.third.org.quartz.JobListener) + * @see #removeListener(String) + * @see #addJobNamePattern(String) + * @see #addJobGroupPattern(String) + * + * @author James House (jhouse AT revolition DOT net) + */ +public class FilterAndBroadcastJobListener implements JobListener { + + private String name; + private List listeners; + private List namePatterns = new LinkedList(); + private List groupPatterns = new LinkedList(); + + /** + * Construct an instance with the given name. + * + * (Remember to add some delegate listeners!) + * + * @param name the name of this instance + */ + public FilterAndBroadcastJobListener(String name) { + if(name == null) { + throw new IllegalArgumentException("Listener name cannot be null!"); + } + this.name = name; + listeners = new LinkedList(); + } + + /** + * Construct an instance with the given name, and List of listeners. + * + * @param name the name of this instance + * @param listeners the initial List of JobListeners to broadcast to. + */ + public FilterAndBroadcastJobListener(String name, List listeners) { + this(name); + this.listeners.addAll(listeners); + } + + public String getName() { + return name; + } + + public void addListener(JobListener listener) { + listeners.add(listener); + } + + public boolean removeListener(JobListener listener) { + return listeners.remove(listener); + } + + public boolean removeListener(String listenerName) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + JobListener jl = (JobListener) itr.next(); + if(jl.getName().equals(listenerName)) { + itr.remove(); + return true; + } + } + return false; + } + + public List getListeners() { + return java.util.Collections.unmodifiableList(listeners); + } + + /** + * If one or more name patterns are specified, only events relating to + * jobs who's name matches the given regular expression pattern + * will be dispatched to the delegate listeners. + * + * @param regularExpression + */ + public void addJobNamePattern(String regularExpression) { + if(regularExpression == null) { + throw new IllegalArgumentException("Expression cannot be null!"); + } + + namePatterns.add(regularExpression); + } + + public List getJobNamePatterns() { + return namePatterns; + } + + /** + * If one or more group patterns are specified, only events relating to + * jobs who's group matches the given regular expression pattern + * will be dispatched to the delegate listeners. + * + * @param regularExpression + */ + public void addJobGroupPattern(String regularExpression) { + if(regularExpression == null) { + throw new IllegalArgumentException("Expression cannot be null!"); + } + + groupPatterns.add(regularExpression); + } + + public List getJobGroupPatterns() { + return namePatterns; + } + + protected boolean shouldDispatch(JobExecutionContext context) { + JobDetail job = context.getJobDetail(); + + if(namePatterns.size() == 0 && groupPatterns.size() == 0) { + return true; + } + + Iterator itr = groupPatterns.iterator(); + while(itr.hasNext()) { + String pat = (String) itr.next(); + if(job.getGroup().matches(pat)) { + return true; + } + } + + itr = namePatterns.iterator(); + while(itr.hasNext()) { + String pat = (String) itr.next(); + if(job.getName().matches(pat)) { + return true; + } + } + + return false; + } + + public void jobToBeExecuted(JobExecutionContext context) { + + if(!shouldDispatch(context)) { + return; + } + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + JobListener jl = (JobListener) itr.next(); + jl.jobToBeExecuted(context); + } + } + + public void jobExecutionVetoed(JobExecutionContext context) { + + if(!shouldDispatch(context)) { + return; + } + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + JobListener jl = (JobListener) itr.next(); + jl.jobExecutionVetoed(context); + } + } + + public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { + + if(!shouldDispatch(context)) { + return; + } + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + JobListener jl = (JobListener) itr.next(); + jl.jobWasExecuted(context, jobException); + } + } + +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/FilterAndBroadcastTriggerListener.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/FilterAndBroadcastTriggerListener.java new file mode 100644 index 000000000..c07cde032 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/FilterAndBroadcastTriggerListener.java @@ -0,0 +1,228 @@ +/* + * Copyright 2004-2006 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.fr.third.org.quartz.listeners; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import com.fr.third.org.quartz.JobExecutionContext; +import com.fr.third.org.quartz.Trigger; +import com.fr.third.org.quartz.TriggerListener; + +/** + * Holds a List of references to TriggerListener instances and broadcasts all + * events to them (in order) - if the event is not excluded via filtering + * (read on). + * + *The broadcasting behavior of this listener to delegate listeners may be + * more convenient than registering all of the listeners directly with the + * Trigger, and provides the flexibility of easily changing which listeners + * get notified.
+ * + *You may also register a number of Regular Expression patterns to match + * the events against. If one or more patterns are registered, the broadcast + * will only take place if the event applies to a trigger who's name/group + * matches one or more of the patterns.
+ * + * @see #addListener(com.fr.third.org.quartz.TriggerListener) + * @see #removeListener(com.fr.third.org.quartz.TriggerListener) + * @see #removeListener(String) + * @see #addTriggerNamePattern(String) + * @see #addTriggerGroupPattern(String) + * + * @author James House (jhouse AT revolition DOT net) + */ +public class FilterAndBroadcastTriggerListener implements TriggerListener { + + private String name; + private List listeners; + private List namePatterns = new LinkedList(); + private List groupPatterns = new LinkedList(); + + /** + * Construct an instance with the given name. + * + * (Remember to add some delegate listeners!) + * + * @param name the name of this instance + */ + public FilterAndBroadcastTriggerListener(String name) { + if(name == null) { + throw new IllegalArgumentException("Listener name cannot be null!"); + } + this.name = name; + listeners = new LinkedList(); + } + + /** + * Construct an instance with the given name, and List of listeners. + * + * @param name the name of this instance + * @param listeners the initial List of TriggerListeners to broadcast to. + */ + public FilterAndBroadcastTriggerListener(String name, List listeners) { + this(name); + this.listeners.addAll(listeners); + } + + public String getName() { + return name; + } + + public void addListener(TriggerListener listener) { + listeners.add(listener); + } + + public boolean removeListener(TriggerListener listener) { + return listeners.remove(listener); + } + + public boolean removeListener(String listenerName) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + TriggerListener l = (TriggerListener) itr.next(); + if(l.getName().equals(listenerName)) { + itr.remove(); + return true; + } + } + return false; + } + + public List getListeners() { + return java.util.Collections.unmodifiableList(listeners); + } + + /** + * If one or more name patterns are specified, only events relating to + * triggers who's name matches the given regular expression pattern + * will be dispatched to the delegate listeners. + * + * @param regularExpression + */ + public void addTriggerNamePattern(String regularExpression) { + if(regularExpression == null) { + throw new IllegalArgumentException("Expression cannot be null!"); + } + + namePatterns.add(regularExpression); + } + + public List getTriggerNamePatterns() { + return namePatterns; + } + + /** + * If one or more group patterns are specified, only events relating to + * triggers who's group matches the given regular expression pattern + * will be dispatched to the delegate listeners. + * + * @param regularExpression + */ + public void addTriggerGroupPattern(String regularExpression) { + if(regularExpression == null) { + throw new IllegalArgumentException("Expression cannot be null!"); + } + + groupPatterns.add(regularExpression); + } + + public List getTriggerGroupPatterns() { + return namePatterns; + } + + protected boolean shouldDispatch(Trigger trigger) { + + if(namePatterns.size() == 0 && groupPatterns.size() == 0) { + return true; + } + + Iterator itr = groupPatterns.iterator(); + while(itr.hasNext()) { + String pat = (String) itr.next(); + if(trigger.getGroup().matches(pat)) { + return true; + } + } + + itr = namePatterns.iterator(); + while(itr.hasNext()) { + String pat = (String) itr.next(); + if(trigger.getName().matches(pat)) { + return true; + } + } + + return false; + } + + public void triggerFired(Trigger trigger, JobExecutionContext context) { + + if(!shouldDispatch(trigger)) { + return; + } + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + TriggerListener l = (TriggerListener) itr.next(); + l.triggerFired(trigger, context); + } + } + + public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { + + if(!shouldDispatch(trigger)) { + return false; + } + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + TriggerListener l = (TriggerListener) itr.next(); + if(l.vetoJobExecution(trigger, context)) { + return true; + } + } + return false; + } + + public void triggerMisfired(Trigger trigger) { + + if(!shouldDispatch(trigger)) { + return; + } + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + TriggerListener l = (TriggerListener) itr.next(); + l.triggerMisfired(trigger); + } + } + + public void triggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode) { + + if(!shouldDispatch(trigger)) { + return; + } + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + TriggerListener l = (TriggerListener) itr.next(); + l.triggerComplete(trigger, context, triggerInstructionCode); + } + } + +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/JobChainingJobListener.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/JobChainingJobListener.java new file mode 100644 index 000000000..8eb6c3105 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/JobChainingJobListener.java @@ -0,0 +1,108 @@ +/* + * Copyright 2004-2006 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.fr.third.org.quartz.listeners; + +import java.util.HashMap; +import java.util.Map; + +import com.fr.third.org.quartz.utils.Key; +import com.fr.third.org.quartz.JobExecutionContext; +import com.fr.third.org.quartz.JobExecutionException; +import com.fr.third.org.quartz.SchedulerException; + +/** + * Keeps a collection of mappings of which Job to trigger after the completion + * of a given job. If this listener is notified of a job completing that has a + * mapping, then it will then attempt to trigger the follow-up job. This + * achieves "job chaining", or a "poor man's workflow". + * + *Generally an instance of this listener would be registered as a global + * job listener, rather than being registered directly to a given job.
+ * + *If for some reason there is a failure creating the trigger for the + * follow-up job (which would generally only be caused by a rare serious + * failure in the system, or the non-existence of the follow-up job), an error + * messsage is logged, but no other action is taken. If you need more rigorous + * handling of the error, consider scheduling the triggering of the flow-up + * job within your job itself.
+ * + * @author James House (jhouse AT revolition DOT net) + */ +public class JobChainingJobListener extends JobListenerSupport { + + private String name; + private Map chainLinks; + + + /** + * Construct an instance with the given name. + * + * @param name the name of this instance + */ + public JobChainingJobListener(String name) { + if(name == null) { + throw new IllegalArgumentException("Listener name cannot be null!"); + } + this.name = name; + chainLinks = new HashMap(); + } + + public String getName() { + return name; + } + + /** + * Add a chain mapping - when the Job identified by the first key completes + * the job identified by the second key will be triggered. + * + * @param firstJob a Key with the name and group of the first job + * @param secondJob a Key with the name and group of the follow-up job + */ + public void addJobChainLink(Key firstJob, Key secondJob) { + + if(firstJob == null || secondJob == null) { + throw new IllegalArgumentException("Key cannot be null!"); + } + + if(firstJob.getName() == null || secondJob.getName() == null) { + throw new IllegalArgumentException("Key cannot have a null name!"); + } + + chainLinks.put(firstJob, secondJob); + } + + public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { + + Key sj = (Key) chainLinks.get(context.getJobDetail().getKey()); + + if(sj == null) { + return; + } + + getLog().info("Job '" + context.getJobDetail().getFullName() + "' will now chain to Job '" + sj + "'"); + + try { + if(context.getJobDetail().isVolatile() || context.getTrigger().isVolatile()) { + context.getScheduler().triggerJobWithVolatileTrigger(sj.getName(), sj.getGroup()); + } else { + context.getScheduler().triggerJob(sj.getName(), sj.getGroup()); + } + } catch(SchedulerException se) { + getLog().error("Error encountered during chaining to Job '" + sj + "'", se); + } + } +} + diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/JobListenerSupport.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/JobListenerSupport.java new file mode 100644 index 000000000..36cabd55c --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/JobListenerSupport.java @@ -0,0 +1,60 @@ +/* + * Copyright 2004-2006 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package com.fr.third.org.quartz.listeners; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import com.fr.third.org.quartz.JobListener; +import com.fr.third.org.quartz.JobExecutionContext; +import com.fr.third.org.quartz.JobExecutionException; + +/** + * A helpful abstract base class for implementors of + *{@link com.fr.third.org.quartz.JobListener}
.
+ *
+ *
+ * The methods in this class are empty so you only need to override the
+ * subset for the {@link com.fr.third.org.quartz.JobListener}
events
+ * you care about.
+ *
+ * You are required to implement {@link com.fr.third.org.quartz.JobListener#getName()}
+ * to return the unique name of your JobListener
.
+ *
{@link org.apache.commons.logging.Log}
for this
+ * class's category. This should be used by subclasses for logging.
+ */
+ protected Log getLog() {
+ return log;
+ }
+
+ public void jobToBeExecuted(JobExecutionContext context) {
+ }
+
+ public void jobExecutionVetoed(JobExecutionContext context) {
+ }
+
+ public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/SchedulerListenerSupport.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/SchedulerListenerSupport.java
new file mode 100644
index 000000000..f18565736
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/SchedulerListenerSupport.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2004-2006 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.fr.third.org.quartz.listeners;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.SchedulerListener;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.SchedulerException;
+
+/**
+ * A helpful abstract base class for implementors of
+ * {@link com.fr.third.org.quartz.SchedulerListener}
.
+ *
+ *
+ * The methods in this class are empty so you only need to override the
+ * subset for the {@link com.fr.third.org.quartz.SchedulerListener}
events
+ * you care about.
+ *
{@link org.apache.commons.logging.Log}
for this
+ * class's category. This should be used by subclasses for logging.
+ */
+ protected Log getLog() {
+ return log;
+ }
+
+ public void jobScheduled(Trigger trigger) {
+ }
+
+ public void jobUnscheduled(String triggerName, String triggerGroup) {
+ }
+
+ public void triggerFinalized(Trigger trigger) {
+ }
+
+ public void triggersPaused(String triggerName, String triggerGroup) {
+ }
+
+ public void triggersResumed(String triggerName, String triggerGroup) {
+ }
+
+ public void jobsPaused(String jobName, String jobGroup) {
+ }
+
+ public void jobsResumed(String jobName, String jobGroup) {
+ }
+
+ public void schedulerError(String msg, SchedulerException cause) {
+ }
+
+ public void schedulerShutdown() {
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/TriggerListenerSupport.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/TriggerListenerSupport.java
new file mode 100644
index 000000000..b8c895557
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/listeners/TriggerListenerSupport.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2004-2006 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.fr.third.org.quartz.listeners;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.TriggerListener;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.JobExecutionContext;
+
+/**
+ * A helpful abstract base class for implementors of
+ * {@link com.fr.third.org.quartz.TriggerListener}
.
+ *
+ *
+ * The methods in this class are empty so you only need to override the
+ * subset for the {@link com.fr.third.org.quartz.TriggerListener}
events
+ * you care about.
+ *
+ * You are required to implement {@link com.fr.third.org.quartz.TriggerListener#getName()}
+ * to return the unique name of your TriggerListener
.
+ *
{@link org.apache.commons.logging.Log}
for this
+ * class's category. This should be used by subclasses for logging.
+ */
+ protected Log getLog() {
+ return log;
+ }
+
+ public void triggerFired(Trigger trigger, JobExecutionContext context) {
+ }
+
+ public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
+ return false;
+ }
+
+ public void triggerMisfired(Trigger trigger) {
+ }
+
+ public void triggerComplete(
+ Trigger trigger,
+ JobExecutionContext context,
+ int triggerInstructionCode) {
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/package.html b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/package.html
new file mode 100644
index 000000000..73e9635f9
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/package.html
@@ -0,0 +1,15 @@
+
+
+The main package of Quartz, containing the client-side interfaces.
+ +UserTransaction
. This is
+ * often necessary if using the JobStoreCMT and the plugin interacts with
+ * jobs/triggers.
+ *
+ *
+ * The subclass should implement start(UserTransaction) and
+ * shutdown(UserTransaction). The UserTransaction
will be
+ * non-null if property wrapInUserTransaction is set to true.
+ *
+ * For convenience, this base class also provides an initialize() implementation + * which saves the scheduler and plugin name, as well as getLog() for logging. + *
+ */ +public abstract class SchedulerPluginWithUserTransactionSupport implements + SchedulerPlugin { + + private String name; + private Scheduler scheduler; + private final Log log = LogFactory.getLog(getClass()); + + // Properties + + private boolean wrapInUserTransaction = false; + + /** + *
+ * Called when the associated Scheduler
is started, in order
+ * to let the plug-in know it can now make calls into the scheduler if it
+ * needs to.
+ *
+ * If UserTransaction is not null, the plugin can call setRollbackOnly() + * on it to signal that the wrapped transaction should rollback. + *
+ * + * @param userTransaction The UserTranaction object used to provide a + * transaction around the start() operation. It will be null if + * wrapInUserTransaction is false or if the transaction failed + * to be started. + */ + protected void start(UserTransaction userTransaction) { + } + + /** + *
+ * Called in order to inform the SchedulerPlugin
that it
+ * should free up all of it's resources because the scheduler is shutting
+ * down.
+ *
+ * If UserTransaction is not null, the plugin can call setRollbackOnly() + * on it to signal that the wrapped transaction should rollback. + *
+ * + * @param userTransaction The UserTranaction object used to provide a + * transaction around the shutdown() operation. It will be null if + * wrapInUserTransaction is false or if the transaction failed + * to be started. + */ + protected void shutdown(UserTransaction userTransaction) { + } + + /** + * Get the commons Log for this class. + */ + protected Log getLog() { + return log; + } + + /** + * Get the name of this plugin. Set as part of initialize(). + */ + protected String getName() { + return name; + } + + /** + * Get this plugin'sScheduler
. Set as part of initialize().
+ */
+ protected Scheduler getScheduler() {
+ return scheduler;
+ }
+
+ public void initialize(String name, Scheduler scheduler) throws SchedulerException {
+ this.name = name;
+ this.scheduler = scheduler;
+ }
+
+ /**
+ * Wrap the start() and shutdown() methods in a UserTransaction. This is necessary
+ * for some plugins if using the JobStoreCMT.
+ */
+ public boolean getWrapInUserTransaction() {
+ return wrapInUserTransaction;
+ }
+
+ /**
+ * Wrap the start() and shutdown() methods in a UserTransaction. This is necessary
+ * for some plugins if using the JobStoreCMT.
+ */
+ public void setWrapInUserTransaction(boolean wrapInUserTransaction) {
+ this.wrapInUserTransaction = wrapInUserTransaction;
+ }
+
+ /**
+ * Based on the value of wrapInUserTransaction, wraps the
+ * call to start(UserTransaction) in a UserTransaction.
+ */
+ public void start() {
+ UserTransaction userTransaction = startUserTransaction();
+ try {
+ start(userTransaction);
+ } finally {
+ resolveUserTransaction(userTransaction);
+ }
+ }
+
+ /**
+ * Based on the value of wrapInUserTransaction, wraps the
+ * call to shutdown(UserTransaction) in a UserTransaction.
+ */
+ public void shutdown() {
+ UserTransaction userTransaction = startUserTransaction();
+ try {
+ shutdown(userTransaction);
+ } finally {
+ resolveUserTransaction(userTransaction);
+ }
+ }
+
+ /**
+ * If wrapInUserTransaction is true, starts a new UserTransaction
+ * and returns it. Otherwise, or if establishing the transaction fail, it
+ * will return null.
+ */
+ private UserTransaction startUserTransaction() {
+ if (wrapInUserTransaction == false) {
+ return null;
+ }
+
+ UserTransaction userTransaction = null;
+ try {
+ userTransaction = UserTransactionHelper.lookupUserTransaction();
+ userTransaction.begin();
+ } catch (Throwable t) {
+ UserTransactionHelper.returnUserTransaction(userTransaction);
+ userTransaction = null;
+ getLog().error("Failed to start UserTransaction for plugin: " + getName(), t);
+ }
+
+ return userTransaction;
+ }
+
+ /**
+ * If the given UserTransaction is not null, it is committed/rolledback,
+ * and then returned to the UserTransactionHelper.
+ */
+ private void resolveUserTransaction(UserTransaction userTransaction) {
+ if (userTransaction != null) {
+ try {
+ if (userTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) {
+ userTransaction.rollback();
+ } else {
+ userTransaction.commit();
+ }
+ } catch (Throwable t) {
+ getLog().error("Failed to resolve UserTransaction for plugin: " + getName(), t);
+ } finally {
+ UserTransactionHelper.returnUserTransaction(userTransaction);
+ }
+ }
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/plugins/history/LoggingJobHistoryPlugin.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/plugins/history/LoggingJobHistoryPlugin.java
new file mode 100644
index 000000000..7ec7e7fc1
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/plugins/history/LoggingJobHistoryPlugin.java
@@ -0,0 +1,542 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.plugins.history;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.JobExecutionContext;
+import com.fr.third.org.quartz.JobExecutionException;
+import com.fr.third.org.quartz.Scheduler;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.JobListener;
+import com.fr.third.org.quartz.spi.SchedulerPlugin;
+
+import java.text.MessageFormat;
+
+/**
+ * Logs a history of all job executions (and execution vetos) via the
+ * Jakarta Commons-Logging framework.
+ *
+ *
+ * The logged message is customizable by setting one of the following message
+ * properties to a String that conforms to the syntax of java.util.MessageFormat
.
+ *
+ * JobToBeFiredMessage - available message data are:
Element | + *Data Type | + *Description | + *
---|---|---|
0 | + *String | + *The Job's Name. | + *
1 | + *String | + *The Job's Group. | + *
2 | + *Date | + *The current time. | + *
3 | + *String | + *The Trigger's name. | + *
4 | + *String | + *The Triggers's group. | + *
5 | + *Date | + *The scheduled fire time. | + *
6 | + *Date | + *The next scheduled fire time. | + *
7 | + *Integer | + *The re-fire count from the JobExecutionContext. | + *
+ * JobSuccessMessage - available message data are:
Element | + *Data Type | + *Description | + *
---|---|---|
0 | + *String | + *The Job's Name. | + *
1 | + *String | + *The Job's Group. | + *
2 | + *Date | + *The current time. | + *
3 | + *String | + *The Trigger's name. | + *
4 | + *String | + *The Triggers's group. | + *
5 | + *Date | + *The scheduled fire time. | + *
6 | + *Date | + *The next scheduled fire time. | + *
7 | + *Integer | + *The re-fire count from the JobExecutionContext. | + *
8 | + *Object | + *The string value (toString() having been called) of the result (if any) + * that the Job set on the JobExecutionContext, with on it. "NULL" if no + * result was set. | + * + *
+ * JobFailedMessage - available message data are:
Element | + *Data Type | + *Description | + *
---|---|---|
0 | + *String | + *The Job's Name. | + *
1 | + *String | + *The Job's Group. | + *
2 | + *Date | + *The current time. | + *
3 | + *String | + *The Trigger's name. | + *
4 | + *String | + *The Triggers's group. | + *
5 | + *Date | + *The scheduled fire time. | + *
6 | + *Date | + *The next scheduled fire time. | + *
7 | + *Integer | + *The re-fire count from the JobExecutionContext. | + *
8 | + *String | + *The message from the thrown JobExecution Exception. + * | + *
+ * JobWasVetoedMessage - available message data are:
Element | + *Data Type | + *Description | + *
---|---|---|
0 | + *String | + *The Job's Name. | + *
1 | + *String | + *The Job's Group. | + *
2 | + *Date | + *The current time. | + *
3 | + *String | + *The Trigger's name. | + *
4 | + *String | + *The Triggers's group. | + *
5 | + *Date | + *The scheduled fire time. | + *
6 | + *Date | + *The next scheduled fire time. | + *
7 | + *Integer | + *The re-fire count from the JobExecutionContext. | + *
+ * Called during creation of the Scheduler
in order to give
+ * the SchedulerPlugin
a chance to initialize.
+ *
+ * Called in order to inform the SchedulerPlugin
that it
+ * should free up all of it's resources because the scheduler is shutting
+ * down.
+ *
+ * The logged message is customizable by setting one of the following message
+ * properties to a String that conforms to the syntax of java.util.MessageFormat
.
+ *
+ * TriggerFiredMessage - available message data are:
Element | + *Data Type | + *Description | + *
---|---|---|
0 | + *String | + *The Trigger's Name. | + *
1 | + *String | + *The Trigger's Group. | + *
2 | + *Date | + *The scheduled fire time. | + *
3 | + *Date | + *The next scheduled fire time. | + *
4 | + *Date | + *The actual fire time. | + *
5 | + *String | + *The Job's name. | + *
6 | + *String | + *The Job's group. | + *
7 | + *Integer | + *The re-fire count from the JobExecutionContext. | + *
+ * TriggerMisfiredMessage - available message data are:
Element | + *Data Type | + *Description | + *
---|---|---|
0 | + *String | + *The Trigger's Name. | + *
1 | + *String | + *The Trigger's Group. | + *
2 | + *Date | + *The scheduled fire time. | + *
3 | + *Date | + *The next scheduled fire time. | + *
4 | + *Date | + *The actual fire time. (the time the misfire was detected/handled) | + *
5 | + *String | + *The Job's name. | + *
6 | + *String | + *The Job's group. | + *
+ * TriggerCompleteMessage - available message data are:
Element | + *Data Type | + *Description | + *
---|---|---|
0 | + *String | + *The Trigger's Name. | + *
1 | + *String | + *The Trigger's Group. | + *
2 | + *Date | + *The scheduled fire time. | + *
3 | + *Date | + *The next scheduled fire time. | + *
4 | + *Date | + *The job completion time. | + *
5 | + *String | + *The Job's name. | + *
6 | + *String | + *The Job's group. | + *
7 | + *Integer | + *The re-fire count from the JobExecutionContext. | + *
8 | + *Integer | + *The trigger's resulting instruction code. | + *
9 | + *String | + *A human-readable translation of the trigger's resulting instruction + * code. | + *
+ * Called during creation of the Scheduler
in order to give
+ * the SchedulerPlugin
a chance to initialize.
+ *
+ * Called in order to inform the SchedulerPlugin
that it
+ * should free up all of it's resources because the scheduler is shutting
+ * down.
+ *
+ * The default value is true
.
+ *
+ * The default value is true
.
+ *
+ * Called during creation of the Scheduler
in order to give
+ * the SchedulerPlugin
a chance to initialize.
+ *
+ * Called in order to inform the SchedulerPlugin
that it
+ * should free up all of it's resources because the scheduler is shutting
+ * down.
+ *
ClassLoadHelper
uses all of the ClassLoadHelper
+ * types that are found in this package in its attempts to load a class, when
+ * one scheme is found to work, it is promoted to the scheme that will be used
+ * first the next time a class is loaded (in order to improve performance).
+ *
+ * + * This approach is used because of the wide variance in class loader behavior + * between the various environments in which Quartz runs (e.g. disparate + * application servers, stand-alone, mobile devices, etc.). Because of this + * disparity, Quartz ran into difficulty with a one class-load style fits-all + * design. Thus, this class loader finds the approach that works, then + * 'remembers' it. + *
+ * + * @see com.fr.third.org.quartz.spi.ClassLoadHelper + * @see com.fr.third.org.quartz.simpl.LoadingLoaderClassLoadHelper + * @see com.fr.third.org.quartz.simpl.SimpleClassLoadHelper + * @see com.fr.third.org.quartz.simpl.ThreadContextClassLoadHelper + * @see com.fr.third.org.quartz.simpl.InitThreadContextClassLoadHelper + * + * @author jhouse + * @author pl47ypus + */ +public class CascadingClassLoadHelper implements ClassLoadHelper { + + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private LinkedList loadHelpers; + + private ClassLoadHelper bestCandidate; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Called to give the ClassLoadHelper a chance to initialize itself, + * including the opportunity to "steal" the class loader off of the calling + * thread, which is the thread that is initializing Quartz. + */ + public void initialize() { + loadHelpers = new LinkedList(); + + loadHelpers.add(new LoadingLoaderClassLoadHelper()); + loadHelpers.add(new SimpleClassLoadHelper()); + loadHelpers.add(new ThreadContextClassLoadHelper()); + loadHelpers.add(new InitThreadContextClassLoadHelper()); + + Iterator iter = loadHelpers.iterator(); + while (iter.hasNext()) { + ClassLoadHelper loadHelper = (ClassLoadHelper) iter.next(); + loadHelper.initialize(); + } + } + + /** + * Return the class with the given name. + */ + public Class loadClass(String name) throws ClassNotFoundException { + + if (bestCandidate != null) { + try { + return bestCandidate.loadClass(name); + } catch (Exception e) { + bestCandidate = null; + } + } + + ClassNotFoundException cnfe = null; + Class clazz = null; + ClassLoadHelper loadHelper = null; + + Iterator iter = loadHelpers.iterator(); + while (iter.hasNext()) { + loadHelper = (ClassLoadHelper) iter.next(); + + try { + clazz = loadHelper.loadClass(name); + break; + } catch (ClassNotFoundException e) { + cnfe = e; + } + } + + if (clazz == null) { + throw cnfe; + } + + bestCandidate = loadHelper; + + return clazz; + } + + /** + * Finds a resource with a given name. This method returns null if no + * resource with this name is found. + * @param name name of the desired resource + * @return a java.net.URL object + */ + public URL getResource(String name) { + + if (bestCandidate != null) { + try { + return bestCandidate.getResource(name); + } catch (Exception e) { + bestCandidate = null; + } + } + + URL result = null; + ClassLoadHelper loadHelper = null; + + Iterator iter = loadHelpers.iterator(); + while (iter.hasNext()) { + loadHelper = (ClassLoadHelper) iter.next(); + + result = loadHelper.getResource(name); + if (result != null) { + break; + } + } + + bestCandidate = loadHelper; + return result; + + } + + /** + * Finds a resource with a given name. This method returns null if no + * resource with this name is found. + * @param name name of the desired resource + * @return a java.io.InputStream object + */ + public InputStream getResourceAsStream(String name) { + + if (bestCandidate != null) { + try { + return bestCandidate.getResourceAsStream(name); + } catch (Exception e) { + bestCandidate = null; + } + } + + InputStream result = null; + ClassLoadHelper loadHelper = null; + + Iterator iter = loadHelpers.iterator(); + while (iter.hasNext()) { + loadHelper = (ClassLoadHelper) iter.next(); + + result = loadHelper.getResourceAsStream(name); + if (result != null) { + break; + } + } + + bestCandidate = loadHelper; + return result; + + } + + /** + * Enable sharing of the class-loader with 3rd party (e.g. digester). + * + * @return the class-loader user be the helper. + */ + public ClassLoader getClassLoader() { + return (this.bestCandidate == null) ? + Thread.currentThread().getContextClassLoader() : + this.bestCandidate.getClassLoader(); + } + +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/CascadingClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/CascadingClassLoadHelper.java.bak new file mode 100644 index 000000000..4ed568acf --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/CascadingClassLoadHelper.java.bak @@ -0,0 +1,202 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package org.quartz.simpl; + +import java.util.Iterator; +import java.util.LinkedList; +import java.net.URL; +import java.io.InputStream; + +import org.quartz.spi.ClassLoadHelper; + +/** + * AClassLoadHelper
uses all of the ClassLoadHelper
+ * types that are found in this package in its attempts to load a class, when
+ * one scheme is found to work, it is promoted to the scheme that will be used
+ * first the next time a class is loaded (in order to improve perfomance).
+ *
+ * + * This approach is used because of the wide variance in class loader behavior + * between the various environments in which Quartz runs (e.g. disparate + * application servers, stand-alone, mobile devices, etc.). Because of this + * disparity, Quartz ran into difficulty with a one class-load style fits-all + * design. Thus, this class loader finds the approach that works, then + * 'remembers' it. + *
+ * + * @see org.quartz.spi.ClassLoadHelper + * @see org.quartz.simpl.LoadingLoaderClassLoadHelper + * @see org.quartz.simpl.SimpleClassLoadHelper + * @see org.quartz.simpl.ThreadContextClassLoadHelper + * @see org.quartz.simpl.InitThreadContextClassLoadHelper + * + * @author jhouse + */ +public class CascadingClassLoadHelper implements ClassLoadHelper { + + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private LinkedList loadHelpers; + + private ClassLoadHelper bestCandidate; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Called to give the ClassLoadHelper a chance to initialize itself, + * including the oportunity to "steal" the class loader off of the calling + * thread, which is the thread that is initializing Quartz. + */ + public void initialize() { + loadHelpers = new LinkedList(); + + loadHelpers.add(new LoadingLoaderClassLoadHelper()); + loadHelpers.add(new SimpleClassLoadHelper()); + loadHelpers.add(new ThreadContextClassLoadHelper()); + loadHelpers.add(new InitThreadContextClassLoadHelper()); + + Iterator iter = loadHelpers.iterator(); + while (iter.hasNext()) { + ClassLoadHelper loadHelper = (ClassLoadHelper) iter.next(); + loadHelper.initialize(); + } + } + + /** + * Return the class with the given name. + */ + public Class loadClass(String name) throws ClassNotFoundException { + + if (bestCandidate != null) { + try { + return bestCandidate.loadClass(name); + } catch (Exception e) { + bestCandidate = null; + } + } + + ClassNotFoundException cnfe = null; + Class clazz = null; + ClassLoadHelper loadHelper = null; + + Iterator iter = loadHelpers.iterator(); + while (iter.hasNext()) { + loadHelper = (ClassLoadHelper) iter.next(); + + try { + clazz = loadHelper.loadClass(name); + break; + } catch (ClassNotFoundException e) { + cnfe = e; + } + } + + if (clazz == null) { + throw cnfe; + } + + bestCandidate = loadHelper; + + return clazz; + } + + /** + * Finds a resource with a given name. This method returns null if no + * resource with this name is found. + * @param name name of the desired resource + * @return a java.net.URL object + */ + public URL getResource(String name) { + + if (bestCandidate != null) { + try { + return bestCandidate.getResource(name); + } catch (Exception e) { + bestCandidate = null; + } + } + + URL result = null; + ClassLoadHelper loadHelper = null; + + Iterator iter = loadHelpers.iterator(); + while (iter.hasNext()) { + loadHelper = (ClassLoadHelper) iter.next(); + + result = loadHelper.getResource(name); + if (result != null) { + break; + } + } + + bestCandidate = loadHelper; + return result; + + } + + /** + * Finds a resource with a given name. This method returns null if no + * resource with this name is found. + * @param name name of the desired resource + * @return a java.io.InputStream object + */ + public InputStream getResourceAsStream(String name) { + + if (bestCandidate != null) { + try { + return bestCandidate.getResourceAsStream(name); + } catch (Exception e) { + bestCandidate = null; + } + } + + InputStream result = null; + ClassLoadHelper loadHelper = null; + + Iterator iter = loadHelpers.iterator(); + while (iter.hasNext()) { + loadHelper = (ClassLoadHelper) iter.next(); + + result = loadHelper.getResourceAsStream(name); + if (result != null) { + break; + } + } + + bestCandidate = loadHelper; + return result; + + } + +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/HostnameInstanceIdGenerator.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/HostnameInstanceIdGenerator.java new file mode 100644 index 000000000..3abd798c0 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/HostnameInstanceIdGenerator.java @@ -0,0 +1,48 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ +package com.fr.third.org.quartz.simpl; + +import java.net.InetAddress; + +import com.fr.third.org.quartz.SchedulerException; +import com.fr.third.org.quartz.spi.InstanceIdGenerator; + +/** + *
+ * InstanceIdGenerator
that names the scheduler instance using
+ * just the machine hostname.
+ *
+ * This class is useful when you know that your scheduler instance will be the + * only one running on a particular machine. Each time the scheduler is + * restarted, it will get the same instance id as long as the machine is not + * renamed. + *
+ * + * @see InstanceIdGenerator + * @see SimpleInstanceIdGenerator + */ +public class HostnameInstanceIdGenerator implements InstanceIdGenerator { + public String generateInstanceId() throws SchedulerException { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (Exception e) { + throw new SchedulerException("Couldn't get host name!", e); + } + } +} \ No newline at end of file diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/InitThreadContextClassLoadHelper.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/InitThreadContextClassLoadHelper.java new file mode 100644 index 000000000..a5906b729 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/InitThreadContextClassLoadHelper.java @@ -0,0 +1,106 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.simpl; + +import com.fr.third.org.quartz.spi.ClassLoadHelper; + +import java.net.URL; +import java.io.InputStream; + +/** + * AClassLoadHelper
that uses either the context class loader
+ * of the thread that initialized Quartz.
+ *
+ * @see com.fr.third.org.quartz.spi.ClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.ThreadContextClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.SimpleClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.CascadingClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.LoadingLoaderClassLoadHelper
+ *
+ * @author jhouse
+ * @author pl47ypus
+ */
+public class InitThreadContextClassLoadHelper implements ClassLoadHelper {
+
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private ClassLoader initClassLoader;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Called to give the ClassLoadHelper a chance to initialize itself,
+ * including the opportunity to "steal" the class loader off of the calling
+ * thread, which is the thread that is initializing Quartz.
+ */
+ public void initialize() {
+ initClassLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ /**
+ * Return the class with the given name.
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return initClassLoader.loadClass(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.net.URL object
+ */
+ public URL getResource(String name) {
+ return initClassLoader.getResource(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.io.InputStream object
+ */
+ public InputStream getResourceAsStream(String name) {
+ return initClassLoader.getResourceAsStream(name);
+ }
+
+ /**
+ * Enable sharing of the class-loader with 3rd party (e.g. digester).
+ *
+ * @return the class-loader user be the helper.
+ */
+ public ClassLoader getClassLoader() {
+ return this.initClassLoader;
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/InitThreadContextClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/InitThreadContextClassLoadHelper.java.bak
new file mode 100644
index 000000000..9c6f84d1e
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/InitThreadContextClassLoadHelper.java.bak
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.simpl;
+
+import org.quartz.spi.ClassLoadHelper;
+
+import java.net.URL;
+import java.io.InputStream;
+
+/**
+ * A ClassLoadHelper
that uses either the context class loader
+ * of the thread that initialized Quartz.
+ *
+ * @see org.quartz.spi.ClassLoadHelper
+ * @see org.quartz.simpl.ThreadContextClassLoadHelper
+ * @see org.quartz.simpl.SimpleClassLoadHelper
+ * @see org.quartz.simpl.CascadingClassLoadHelper
+ * @see org.quartz.simpl.LoadingLoaderClassLoadHelper
+ *
+ * @author jhouse
+ */
+public class InitThreadContextClassLoadHelper implements ClassLoadHelper {
+
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private ClassLoader initClassLoader;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Called to give the ClassLoadHelper a chance to initialize itself,
+ * including the oportunity to "steal" the class loader off of the calling
+ * thread, which is the thread that is initializing Quartz.
+ */
+ public void initialize() {
+ initClassLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ /**
+ * Return the class with the given name.
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return initClassLoader.loadClass(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.net.URL object
+ */
+ public URL getResource(String name) {
+ return initClassLoader.getResource(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.io.InputStream object
+ */
+ public InputStream getResourceAsStream(String name) {
+ return initClassLoader.getResourceAsStream(name);
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/LoadingLoaderClassLoadHelper.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/LoadingLoaderClassLoadHelper.java
new file mode 100644
index 000000000..b61c2af89
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/LoadingLoaderClassLoadHelper.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.simpl;
+
+import com.fr.third.org.quartz.spi.ClassLoadHelper;
+
+import java.net.URL;
+import java.io.InputStream;
+
+/**
+ * A ClassLoadHelper
that uses either the loader of it's own
+ * class (this.getClass().getClassLoader().loadClass( .. )
).
+ *
+ * @see com.fr.third.org.quartz.spi.ClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.InitThreadContextClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.SimpleClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.CascadingClassLoadHelper
+ *
+ * @author jhouse
+ * @author pl47ypus
+ */
+public class LoadingLoaderClassLoadHelper implements ClassLoadHelper {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Called to give the ClassLoadHelper a chance to initialize itself,
+ * including the opportunity to "steal" the class loader off of the calling
+ * thread, which is the thread that is initializing Quartz.
+ */
+ public void initialize() {
+ }
+
+ /**
+ * Return the class with the given name.
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return getClassLoader().loadClass(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.net.URL object
+ */
+ public URL getResource(String name) {
+ return getClassLoader().getResource(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.io.InputStream object
+ */
+ public InputStream getResourceAsStream(String name) {
+ return getClassLoader().getResourceAsStream(name);
+ }
+
+ /**
+ * Enable sharing of the class-loader with 3rd party (e.g. digester).
+ *
+ * @return the class-loader user be the helper.
+ */
+ public ClassLoader getClassLoader() {
+ return this.getClass().getClassLoader();
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/LoadingLoaderClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/LoadingLoaderClassLoadHelper.java.bak
new file mode 100644
index 000000000..8121baa7c
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/LoadingLoaderClassLoadHelper.java.bak
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.simpl;
+
+import org.quartz.spi.ClassLoadHelper;
+
+import java.net.URL;
+import java.io.InputStream;
+
+/**
+ * A ClassLoadHelper
that uses either the loader of it's own
+ * class (this.getClass().getClassLoader().loadClass( .. )
).
+ *
+ * @see org.quartz.spi.ClassLoadHelper
+ * @see org.quartz.simpl.InitThreadContextClassLoadHelper
+ * @see org.quartz.simpl.SimpleClassLoadHelper
+ * @see org.quartz.simpl.CascadingClassLoadHelper
+ *
+ * @author jhouse
+ */
+public class LoadingLoaderClassLoadHelper implements ClassLoadHelper {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Called to give the ClassLoadHelper a chance to initialize itself,
+ * including the oportunity to "steal" the class loader off of the calling
+ * thread, which is the thread that is initializing Quartz.
+ */
+ public void initialize() {
+ }
+
+ /**
+ * Return the class with the given name.
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return getClassLoader().loadClass(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.net.URL object
+ */
+ public URL getResource(String name) {
+ return getClassLoader().getResource(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.io.InputStream object
+ */
+ public InputStream getResourceAsStream(String name) {
+ return getClassLoader().getResourceAsStream(name);
+ }
+
+ private ClassLoader getClassLoader() {
+ return this.getClass().getClassLoader();
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/PropertySettingJobFactory.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/PropertySettingJobFactory.java
new file mode 100644
index 000000000..fe6c29e90
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/PropertySettingJobFactory.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package com.fr.third.org.quartz.simpl;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import com.fr.third.org.quartz.Job;
+import com.fr.third.org.quartz.JobDataMap;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.spi.TriggerFiredBundle;
+
+
+
+/**
+ * A JobFactory that instantiates the Job instance (using the default no-arg
+ * constructor, or more specifically: class.newInstance()
), and
+ * then attempts to set all values in the JobExecutionContext
's
+ * JobDataMap
onto bean properties of the Job
.
+ *
+ * @see com.fr.third.org.quartz.spi.JobFactory
+ * @see SimpleJobFactory
+ * @see com.fr.third.org.quartz.JobExecutionContext#getMergedJobDataMap()
+ * @see #setWarnIfPropertyNotFound(boolean)
+ * @see #setThrowIfPropertyNotFound(boolean)
+ *
+ * @author jhouse
+ */
+public class PropertySettingJobFactory extends SimpleJobFactory {
+ private boolean warnIfNotFound = true;
+ private boolean throwIfNotFound = false;
+
+ public Job newJob(TriggerFiredBundle bundle) throws SchedulerException {
+
+ Job job = super.newJob(bundle);
+
+ JobDataMap jobDataMap = new JobDataMap();
+ jobDataMap.putAll(bundle.getJobDetail().getJobDataMap());
+ jobDataMap.putAll(bundle.getTrigger().getJobDataMap());
+
+ setBeanProps(job, jobDataMap);
+
+ return job;
+ }
+
+ protected void setBeanProps(Object obj, JobDataMap data) throws SchedulerException {
+
+ BeanInfo bi = null;
+ try {
+ bi = Introspector.getBeanInfo(obj.getClass());
+ } catch (IntrospectionException e) {
+ handleError("Unable to introspect Job class.", e);
+ }
+
+ PropertyDescriptor[] propDescs = bi.getPropertyDescriptors();
+
+ // Get the wrapped entry set so don't have to incur overhead of wrapping for
+ // dirty flag checking since this is read only access
+ for (Iterator entryIter = data.getWrappedMap().entrySet().iterator(); entryIter.hasNext();) {
+ Map.Entry entry = (Map.Entry)entryIter.next();
+
+ String name = (String)entry.getKey();
+ String c = name.substring(0, 1).toUpperCase(Locale.US);
+ String methName = "set" + c + name.substring(1);
+
+ java.lang.reflect.Method setMeth = getSetMethod(methName, propDescs);
+
+ Class paramType = null;
+ Object o = null;
+
+ try {
+ if (setMeth == null) {
+ handleError(
+ "No setter on Job class " + obj.getClass().getName() +
+ " for property '" + name + "'");
+ continue;
+ }
+
+ paramType = setMeth.getParameterTypes()[0];
+ o = entry.getValue();
+
+ Object parm = null;
+ if (paramType.isPrimitive()) {
+ if (o == null) {
+ handleError(
+ "Cannot set primitive property '" + name +
+ "' on Job class " + obj.getClass().getName() +
+ " to null.");
+ continue;
+ }
+
+ if (paramType.equals(int.class)) {
+ if (o instanceof String) {
+ parm = new Integer((String)o);
+ } else if (o instanceof Integer) {
+ parm = o;
+ }
+ } else if (paramType.equals(long.class)) {
+ if (o instanceof String) {
+ parm = new Long((String)o);
+ } else if (o instanceof Long) {
+ parm = o;
+ }
+ } else if (paramType.equals(float.class)) {
+ if (o instanceof String) {
+ parm = new Float((String)o);
+ } else if (o instanceof Float) {
+ parm = o;
+ }
+ } else if (paramType.equals(double.class)) {
+ if (o instanceof String) {
+ parm = new Double((String)o);
+ } else if (o instanceof Double) {
+ parm = o;
+ }
+ } else if (paramType.equals(boolean.class)) {
+ if (o instanceof String) {
+ parm = new Boolean((String)o);
+ } else if (o instanceof Boolean) {
+ parm = o;
+ }
+ } else if (paramType.equals(byte.class)) {
+ if (o instanceof String) {
+ parm = new Byte((String)o);
+ } else if (o instanceof Byte) {
+ parm = o;
+ }
+ } else if (paramType.equals(short.class)) {
+ if (o instanceof String) {
+ parm = new Short((String)o);
+ } else if (o instanceof Short) {
+ parm = o;
+ }
+ } else if (paramType.equals(char.class)) {
+ if (o instanceof String) {
+ String str = (String)o;
+ if (str.length() == 1) {
+ parm = new Character(str.charAt(0));
+ }
+ } else if (o instanceof Character) {
+ parm = o;
+ }
+ }
+ } else if ((o != null) && (paramType.isAssignableFrom(o.getClass()))) {
+ parm = o;
+ }
+
+ // If the parameter wasn't originally null, but we didn't find a
+ // matching parameter, then we are stuck.
+ if ((o != null) && (parm == null)) {
+ handleError(
+ "The setter on Job class " + obj.getClass().getName() +
+ " for property '" + name +
+ "' expects a " + paramType +
+ " but was given " + o.getClass().getName());
+ continue;
+ }
+
+ setMeth.invoke(obj, new Object[]{ parm });
+ } catch (NumberFormatException nfe) {
+ handleError(
+ "The setter on Job class " + obj.getClass().getName() +
+ " for property '" + name +
+ "' expects a " + paramType +
+ " but was given " + o.getClass().getName(), nfe);
+ } catch (IllegalArgumentException e) {
+ handleError(
+ "The setter on Job class " + obj.getClass().getName() +
+ " for property '" + name +
+ "' expects a " + paramType +
+ " but was given " + o.getClass().getName(), e);
+ } catch (IllegalAccessException e) {
+ handleError(
+ "The setter on Job class " + obj.getClass().getName() +
+ " for property '" + name +
+ "' could not be accessed.", e);
+ } catch (InvocationTargetException e) {
+ handleError(
+ "The setter on Job class " + obj.getClass().getName() +
+ " for property '" + name +
+ "' could not be invoked.", e);
+ }
+ }
+ }
+
+ private void handleError(String message) throws SchedulerException {
+ handleError(message, null);
+ }
+
+ private void handleError(String message, Exception e) throws SchedulerException {
+ if (isThrowIfPropertyNotFound()) {
+ throw new SchedulerException(message, e);
+ }
+
+ if (isWarnIfPropertyNotFound()) {
+ if (e == null) {
+ getLog().warn(message);
+ } else {
+ getLog().warn(message, e);
+ }
+ }
+ }
+
+ private java.lang.reflect.Method getSetMethod(String name,
+ PropertyDescriptor[] props) {
+ for (int i = 0; i < props.length; i++) {
+ java.lang.reflect.Method wMeth = props[i].getWriteMethod();
+
+ if(wMeth == null) {
+ continue;
+ }
+
+ if(wMeth.getParameterTypes().length != 1) {
+ continue;
+ }
+
+ if (wMeth.getName().equals(name)) {
+ return wMeth;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Whether the JobInstantiation should fail and throw and exception if
+ * a key (name) and value (type) found in the JobDataMap does not
+ * correspond to a proptery setter on the Job class.
+ *
+ * @return Returns the throwIfNotFound.
+ */
+ public boolean isThrowIfPropertyNotFound() {
+ return throwIfNotFound;
+ }
+
+ /**
+ * Whether the JobInstantiation should fail and throw and exception if
+ * a key (name) and value (type) found in the JobDataMap does not
+ * correspond to a proptery setter on the Job class.
+ *
+ * @param throwIfNotFound defaults to false
.
+ */
+ public void setThrowIfPropertyNotFound(boolean throwIfNotFound) {
+ this.throwIfNotFound = throwIfNotFound;
+ }
+
+ /**
+ * Whether a warning should be logged if
+ * a key (name) and value (type) found in the JobDataMap does not
+ * correspond to a proptery setter on the Job class.
+ *
+ * @return Returns the warnIfNotFound.
+ */
+ public boolean isWarnIfPropertyNotFound() {
+ return warnIfNotFound;
+ }
+
+ /**
+ * Whether a warning should be logged if
+ * a key (name) and value (type) found in the JobDataMap does not
+ * correspond to a proptery setter on the Job class.
+ *
+ * @param warnIfNotFound defaults to true
.
+ */
+ public void setWarnIfPropertyNotFound(boolean warnIfNotFound) {
+ this.warnIfNotFound = warnIfNotFound;
+ }
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/RAMJobStore.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/RAMJobStore.java
new file mode 100644
index 000000000..116927ee6
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/RAMJobStore.java
@@ -0,0 +1,1615 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.simpl;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.Calendar;
+import com.fr.third.org.quartz.JobDataMap;
+import com.fr.third.org.quartz.JobDetail;
+import com.fr.third.org.quartz.JobPersistenceException;
+import com.fr.third.org.quartz.ObjectAlreadyExistsException;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.core.SchedulingContext;
+import com.fr.third.org.quartz.spi.ClassLoadHelper;
+import com.fr.third.org.quartz.spi.JobStore;
+import com.fr.third.org.quartz.spi.SchedulerSignaler;
+import com.fr.third.org.quartz.spi.TriggerFiredBundle;
+
+/**
+ *
+ * This class implements a {@link com.fr.third.org.quartz.spi.JobStore}
that
+ * utilizes RAM as its storage device.
+ *
+ * As you should know, the ramification of this is that access is extrememly
+ * fast, but the data is completely volatile - therefore this JobStore
+ * should not be used if true persistence between program shutdowns is
+ * required.
+ *
+ * Create a new RAMJobStore
.
+ *
+ * Called by the QuartzScheduler before the JobStore
is
+ * used, in order to give the it a chance to initialize.
+ *
+ * Called by the QuartzScheduler to inform the JobStore
that
+ * it should free up all of it's resources because the scheduler is
+ * shutting down.
+ *
+ * Store the given {@link com.fr.third.org.quartz.JobDetail}
and {@link com.fr.third.org.quartz.Trigger}
.
+ *
JobDetail
to be stored.
+ * @param newTrigger
+ * The Trigger
to be stored.
+ * @throws ObjectAlreadyExistsException
+ * if a Job
with the same name/group already
+ * exists.
+ */
+ public void storeJobAndTrigger(SchedulingContext ctxt, JobDetail newJob,
+ Trigger newTrigger) throws JobPersistenceException {
+ storeJob(ctxt, newJob, false);
+ storeTrigger(ctxt, newTrigger, false);
+ }
+
+ /**
+ *
+ * Store the given {@link com.fr.third.org.quartz.Job}
.
+ *
Job
to be stored.
+ * @param replaceExisting
+ * If true
, any Job
existing in the
+ * JobStore
with the same name & group should be
+ * over-written.
+ * @throws ObjectAlreadyExistsException
+ * if a Job
with the same name/group already
+ * exists, and replaceExisting is set to false.
+ */
+ public void storeJob(SchedulingContext ctxt, JobDetail newJob,
+ boolean replaceExisting) throws ObjectAlreadyExistsException {
+ JobWrapper jw = new JobWrapper((JobDetail)newJob.clone());
+
+ boolean repl = false;
+
+ if (jobsByFQN.get(jw.key) != null) {
+ if (!replaceExisting) {
+ throw new ObjectAlreadyExistsException(newJob);
+ }
+ repl = true;
+ }
+
+ synchronized (triggerLock) {
+ if (!repl) {
+ // get job group
+ HashMap grpMap = (HashMap) jobsByGroup.get(newJob.getGroup());
+ if (grpMap == null) {
+ grpMap = new HashMap(100);
+ jobsByGroup.put(newJob.getGroup(), grpMap);
+ }
+ // add to jobs by group
+ grpMap.put(newJob.getName(), jw);
+ // add to jobs by FQN map
+ jobsByFQN.put(jw.key, jw);
+ } else {
+ // update job detail
+ JobWrapper orig = (JobWrapper) jobsByFQN.get(jw.key);
+ orig.jobDetail = jw.jobDetail; // already cloned
+ }
+ }
+ }
+
+ /**
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Job}
with the given
+ * name, and any {@link com.fr.third.org.quartz.Trigger}
s that reference
+ * it.
+ *
Job
to be removed.
+ * @param groupName
+ * The group name of the Job
to be removed.
+ * @return true
if a Job
with the given name &
+ * group was found and removed from the store.
+ */
+ public boolean removeJob(SchedulingContext ctxt, String jobName,
+ String groupName) {
+ String key = JobWrapper.getJobNameKey(jobName, groupName);
+
+ boolean found = false;
+
+ Trigger[] trigger = getTriggersForJob(ctxt, jobName,
+ groupName);
+ for (int i = 0; i < trigger.length; i++) {
+ Trigger trig = trigger[i];
+ this.removeTrigger(ctxt, trig.getName(), trig.getGroup());
+ found = true;
+ }
+ synchronized (triggerLock) {
+ found = (jobsByFQN.remove(key) != null) | found;
+ if (found) {
+
+ HashMap grpMap = (HashMap) jobsByGroup.get(groupName);
+ if (grpMap != null) {
+ grpMap.remove(jobName);
+ if (grpMap.size() == 0) {
+ jobsByGroup.remove(groupName);
+ }
+ }
+ }
+ }
+
+ return found;
+ }
+
+ /**
+ *
+ * Store the given {@link com.fr.third.org.quartz.Trigger}
.
+ *
Trigger
to be stored.
+ * @param replaceExisting
+ * If true
, any Trigger
existing in
+ * the JobStore
with the same name & group should
+ * be over-written.
+ * @throws ObjectAlreadyExistsException
+ * if a Trigger
with the same name/group already
+ * exists, and replaceExisting is set to false.
+ *
+ * @see #pauseTriggerGroup(SchedulingContext, String)
+ */
+ public void storeTrigger(SchedulingContext ctxt, Trigger newTrigger,
+ boolean replaceExisting) throws JobPersistenceException {
+ TriggerWrapper tw = new TriggerWrapper((Trigger)newTrigger.clone());
+
+ if (triggersByFQN.get(tw.key) != null) {
+ if (!replaceExisting) {
+ throw new ObjectAlreadyExistsException(newTrigger);
+ }
+
+ removeTrigger(ctxt, newTrigger.getName(), newTrigger.getGroup(), false);
+ }
+
+ if (retrieveJob(ctxt, newTrigger.getJobName(), newTrigger.getJobGroup()) == null) {
+ throw new JobPersistenceException("The job ("
+ + newTrigger.getFullJobName()
+ + ") referenced by the trigger does not exist.");
+ }
+
+ synchronized (triggerLock) {
+ // add to triggers array
+ triggers.add(tw);
+ // add to triggers by group
+ HashMap grpMap = (HashMap) triggersByGroup.get(newTrigger
+ .getGroup());
+ if (grpMap == null) {
+ grpMap = new HashMap(100);
+ triggersByGroup.put(newTrigger.getGroup(), grpMap);
+ }
+ grpMap.put(newTrigger.getName(), tw);
+ // add to triggers by FQN map
+ triggersByFQN.put(tw.key, tw);
+
+ if (pausedTriggerGroups.contains(newTrigger.getGroup())
+ || pausedJobGroups.contains(newTrigger.getJobGroup())) {
+ tw.state = TriggerWrapper.STATE_PAUSED;
+ if (blockedJobs.contains(tw.jobKey)) {
+ tw.state = TriggerWrapper.STATE_PAUSED_BLOCKED;
+ }
+ } else if (blockedJobs.contains(tw.jobKey)) {
+ tw.state = TriggerWrapper.STATE_BLOCKED;
+ } else {
+ timeTriggers.add(tw);
+ }
+ }
+ }
+
+ /**
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Trigger}
with the
+ * given name.
+ *
Trigger
to be removed.
+ * @param groupName
+ * The group name of the Trigger
to be removed.
+ * @return true
if a Trigger
with the given
+ * name & group was found and removed from the store.
+ */
+ public boolean removeTrigger(SchedulingContext ctxt, String triggerName,
+ String groupName) {
+ return removeTrigger(ctxt, triggerName, groupName, true);
+ }
+ private boolean removeTrigger(SchedulingContext ctxt, String triggerName,
+ String groupName, boolean removeOrphanedJob) {
+ String key = TriggerWrapper.getTriggerNameKey(triggerName, groupName);
+
+ boolean found = false;
+
+ synchronized (triggerLock) {
+ // remove from triggers by FQN map
+ found = (triggersByFQN.remove(key) == null) ? false : true;
+ if (found) {
+ TriggerWrapper tw = null;
+ // remove from triggers by group
+ HashMap grpMap = (HashMap) triggersByGroup.get(groupName);
+ if (grpMap != null) {
+ grpMap.remove(triggerName);
+ if (grpMap.size() == 0) {
+ triggersByGroup.remove(groupName);
+ }
+ }
+ // remove from triggers array
+ Iterator tgs = triggers.iterator();
+ while (tgs.hasNext()) {
+ tw = (TriggerWrapper) tgs.next();
+ if (key.equals(tw.key)) {
+ tgs.remove();
+ break;
+ }
+ }
+ timeTriggers.remove(tw);
+
+ if (removeOrphanedJob) {
+ JobWrapper jw = (JobWrapper) jobsByFQN.get(JobWrapper
+ .getJobNameKey(tw.trigger.getJobName(), tw.trigger
+ .getJobGroup()));
+ Trigger[] trigs = getTriggersForJob(ctxt, tw.trigger
+ .getJobName(), tw.trigger.getJobGroup());
+ if ((trigs == null || trigs.length == 0) && !jw.jobDetail.isDurable()) {
+ removeJob(ctxt, tw.trigger.getJobName(), tw.trigger
+ .getJobGroup());
+ }
+ }
+ }
+ }
+
+ return found;
+ }
+
+
+ /**
+ * @see com.fr.third.org.quartz.spi.JobStore#replaceTrigger(com.fr.third.org.quartz.core.SchedulingContext, java.lang.String, java.lang.String, com.fr.third.org.quartz.Trigger)
+ */
+ public boolean replaceTrigger(SchedulingContext ctxt, String triggerName, String groupName, Trigger newTrigger) throws JobPersistenceException {
+ String key = TriggerWrapper.getTriggerNameKey(triggerName, groupName);
+
+ boolean found = false;
+
+ synchronized (triggerLock) {
+ // remove from triggers by FQN map
+ TriggerWrapper tw = (TriggerWrapper) triggersByFQN.remove(key);
+ found = ( tw == null) ? false : true;
+
+ if (found) {
+
+ if (!tw.getTrigger().getJobName().equals(newTrigger.getJobName()) ||
+ !tw.getTrigger().getJobGroup().equals(newTrigger.getJobGroup())) {
+ throw new JobPersistenceException("New trigger is not related to the same job as the old trigger.");
+ }
+
+ tw = null;
+ // remove from triggers by group
+ HashMap grpMap = (HashMap) triggersByGroup.get(groupName);
+ if (grpMap != null) {
+ grpMap.remove(triggerName);
+ if (grpMap.size() == 0) {
+ triggersByGroup.remove(groupName);
+ }
+ }
+ // remove from triggers array
+ Iterator tgs = triggers.iterator();
+ while (tgs.hasNext()) {
+ tw = (TriggerWrapper) tgs.next();
+ if (key.equals(tw.key)) {
+ tgs.remove();
+ break;
+ }
+ }
+ timeTriggers.remove(tw);
+
+ try {
+ storeTrigger(ctxt, newTrigger, false);
+ } catch(JobPersistenceException jpe) {
+ storeTrigger(ctxt, tw.getTrigger(), false); // put previous trigger back...
+ throw jpe;
+ }
+ }
+ }
+
+ return found;
+ }
+
+ /**
+ *
+ * Retrieve the {@link com.fr.third.org.quartz.JobDetail}
for the given
+ * {@link com.fr.third.org.quartz.Job}
.
+ *
Job
to be retrieved.
+ * @param groupName
+ * The group name of the Job
to be retrieved.
+ * @return The desired Job
, or null if there is no match.
+ */
+ public JobDetail retrieveJob(SchedulingContext ctxt, String jobName,
+ String groupName) {
+ JobWrapper jw = (JobWrapper) jobsByFQN.get(JobWrapper.getJobNameKey(
+ jobName, groupName));
+
+ return (jw != null) ? (JobDetail)jw.jobDetail.clone() : null;
+ }
+
+ /**
+ *
+ * Retrieve the given {@link com.fr.third.org.quartz.Trigger}
.
+ *
Trigger
to be retrieved.
+ * @param groupName
+ * The group name of the Trigger
to be retrieved.
+ * @return The desired Trigger
, or null if there is no
+ * match.
+ */
+ public Trigger retrieveTrigger(SchedulingContext ctxt, String triggerName,
+ String groupName) {
+ TriggerWrapper tw = (TriggerWrapper) triggersByFQN.get(TriggerWrapper
+ .getTriggerNameKey(triggerName, groupName));
+
+ return (tw != null) ? (Trigger)tw.getTrigger().clone() : null;
+ }
+
+ /**
+ *
+ * Get the current state of the identified {@link Trigger}
.
+ *
+ * Store the given {@link com.fr.third.org.quartz.Calendar}
.
+ *
Calendar
to be stored.
+ * @param replaceExisting
+ * If true
, any Calendar
existing
+ * in the JobStore
with the same name & group
+ * should be over-written.
+ * @param updateTriggers
+ * If true
, any Trigger
s existing
+ * in the JobStore
that reference an existing
+ * Calendar with the same name with have their next fire time
+ * re-computed with the new Calendar
.
+ * @throws ObjectAlreadyExistsException
+ * if a Calendar
with the same name already
+ * exists, and replaceExisting is set to false.
+ */
+ public void storeCalendar(SchedulingContext ctxt, String name,
+ Calendar calendar, boolean replaceExisting, boolean updateTriggers)
+ throws ObjectAlreadyExistsException {
+ Object obj = calendarsByName.get(name);
+
+ if (obj != null && replaceExisting == false) {
+ throw new ObjectAlreadyExistsException(
+ "Calendar with name '" + name + "' already exists.");
+ } else if (obj != null) {
+ calendarsByName.remove(name);
+ }
+
+ calendarsByName.put(name, calendar);
+
+ if(obj != null && updateTriggers) {
+ synchronized (triggerLock) {
+ Iterator trigs = getTriggerWrappersForCalendar(name).iterator();
+ while (trigs.hasNext()) {
+ TriggerWrapper tw = (TriggerWrapper) trigs.next();
+ Trigger trig = tw.getTrigger();
+ boolean removed = timeTriggers.remove(tw);
+
+ trig.updateWithNewCalendar(calendar, getMisfireThreshold());
+
+ if(removed) {
+ timeTriggers.add(tw);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Calendar}
with the
+ * given name.
+ *
+ * If removal of the Calendar
would result in
+ * JobPersistenceException
will be thrown.
Calendar
to be removed.
+ * @return true
if a Calendar
with the given name
+ * was found and removed from the store.
+ */
+ public boolean removeCalendar(SchedulingContext ctxt, String calName)
+ throws JobPersistenceException {
+ int numRefs = 0;
+
+ synchronized (triggerLock) {
+ Iterator itr = triggers.iterator();
+ while (itr.hasNext()) {
+ Trigger trigg = ((TriggerWrapper) itr.next()).trigger;
+ if (trigg.getCalendarName() != null
+ && trigg.getCalendarName().equals(calName)) {
+ numRefs++;
+ }
+ }
+ }
+
+ if (numRefs > 0) {
+ throw new JobPersistenceException(
+ "Calender cannot be removed if it referenced by a Trigger!");
+ }
+
+ return (calendarsByName.remove(calName) != null);
+ }
+
+ /**
+ *
+ * Retrieve the given {@link com.fr.third.org.quartz.Trigger}
.
+ *
Calendar
to be retrieved.
+ * @return The desired Calendar
, or null if there is no
+ * match.
+ */
+ public Calendar retrieveCalendar(SchedulingContext ctxt, String calName) {
+ return (Calendar) calendarsByName.get(calName);
+ }
+
+ /**
+ *
+ * Get the number of {@link com.fr.third.org.quartz.JobDetail}
s that are
+ * stored in the JobsStore
.
+ *
+ * Get the number of {@link com.fr.third.org.quartz.Trigger}
s that are
+ * stored in the JobsStore
.
+ *
+ * Get the number of {@link com.fr.third.org.quartz.Calendar}
s that are
+ * stored in the JobsStore
.
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Job}
s that
+ * have the given group name.
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Calendar}
s
+ * in the JobStore
.
+ *
+ * If there are no Calendars in the given group name, the result should be
+ * a zero-length array (not null
).
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Trigger}
s
+ * that have the given group name.
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Job}
+ * groups.
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Trigger}
+ * groups.
+ *
+ * Get all of the Triggers that are associated to the given Job. + *
+ * + *+ * If there are no matches, a zero-length array should be returned. + *
+ */ + public Trigger[] getTriggersForJob(SchedulingContext ctxt, String jobName, + String groupName) { + ArrayList trigList = new ArrayList(); + + String jobKey = JobWrapper.getJobNameKey(jobName, groupName); + synchronized (triggerLock) { + for (int i = 0; i < triggers.size(); i++) { + TriggerWrapper tw = (TriggerWrapper) triggers.get(i); + if (tw.jobKey.equals(jobKey)) { + trigList.add(tw.trigger.clone()); + } + } + } + + return (Trigger[]) trigList.toArray(new Trigger[trigList.size()]); + } + + protected ArrayList getTriggerWrappersForJob(String jobName, String groupName) { + ArrayList trigList = new ArrayList(); + + String jobKey = JobWrapper.getJobNameKey(jobName, groupName); + synchronized (triggerLock) { + for (int i = 0; i < triggers.size(); i++) { + TriggerWrapper tw = (TriggerWrapper) triggers.get(i); + if (tw.jobKey.equals(jobKey)) { + trigList.add(tw); + } + } + } + + return trigList; + } + + protected ArrayList getTriggerWrappersForCalendar(String calName) { + ArrayList trigList = new ArrayList(); + + synchronized (triggerLock) { + for (int i = 0; i < triggers.size(); i++) { + TriggerWrapper tw = (TriggerWrapper) triggers.get(i); + String tcalName = tw.getTrigger().getCalendarName(); + if (tcalName != null && tcalName.equals(calName)) { + trigList.add(tw); + } + } + } + + return trigList; + } + + /** + *
+ * Pause the {@link Trigger}
with the given name.
+ *
+ * Pause all of the {@link Trigger}s
in the given group.
+ *
+ * The JobStore should "remember" that the group is paused, and impose the + * pause on any new triggers that are added to the group while the group is + * paused. + *
+ * + */ + public void pauseTriggerGroup(SchedulingContext ctxt, String groupName) { + + synchronized (triggerLock) { + if (pausedTriggerGroups.contains(groupName)) { + return; + } + + pausedTriggerGroups.add(groupName); + String[] names = getTriggerNames(ctxt, groupName); + + for (int i = 0; i < names.length; i++) { + pauseTrigger(ctxt, names[i], groupName); + } + } + } + + /** + *
+ * Pause the {@link com.fr.third.org.quartz.JobDetail}
with the given
+ * name - by pausing all of its current Trigger
s.
+ *
+ * Pause all of the {@link com.fr.third.org.quartz.JobDetail}s
in the
+ * given group - by pausing all of their Trigger
s.
+ *
+ * The JobStore should "remember" that the group is paused, and impose the + * pause on any new jobs that are added to the group while the group is + * paused. + *
+ */ + public void pauseJobGroup(SchedulingContext ctxt, String groupName) { + synchronized (triggerLock) { + if (!pausedJobGroups.contains(groupName)) { + pausedJobGroups.add(groupName); + } + + String[] jobNames = getJobNames(ctxt, groupName); + + for (int i = 0; i < jobNames.length; i++) { + Trigger[] triggers = getTriggersForJob(ctxt, jobNames[i], + groupName); + for (int j = 0; j < triggers.length; j++) { + pauseTrigger(ctxt, triggers[j].getName(), + triggers[j].getGroup()); + } + } + } + } + + /** + *
+ * Resume (un-pause) the {@link Trigger}
with the given
+ * name.
+ *
+ * If the Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link Trigger}s
in the
+ * given group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) the {@link com.fr.third.org.quartz.JobDetail}
with
+ * the given name.
+ *
+ * If any of the Job
'sTrigger
s missed one
+ * or more fire-times, then the Trigger
's misfire
+ * instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link com.fr.third.org.quartz.JobDetail}s
+ * in the given group.
+ *
+ * If any of the Job
s had Trigger
s that
+ * missed one or more fire-times, then the Trigger
's
+ * misfire instruction will be applied.
+ *
+ * Pause all triggers - equivalent of calling pauseTriggerGroup(group)
+ * on every group.
+ *
+ * When resumeAll()
is called (to un-pause), trigger misfire
+ * instructions WILL be applied.
+ *
+ * Resume (un-pause) all triggers - equivalent of calling resumeTriggerGroup(group)
+ * on every group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Get a handle to the next trigger to be fired, and mark it as 'reserved' + * by the calling scheduler. + *
+ * + * @see #releaseAcquiredTrigger(SchedulingContext, Trigger) + */ + public Trigger acquireNextTrigger(SchedulingContext ctxt, long noLaterThan) { + TriggerWrapper tw = null; + + synchronized (triggerLock) { + + while (tw == null) { + try { + tw = (TriggerWrapper) timeTriggers.first(); + } catch (java.util.NoSuchElementException nsee) { + return null; + } + + if (tw == null) { + return null; + } + + if (tw.trigger.getNextFireTime() == null) { + timeTriggers.remove(tw); + tw = null; + continue; + } + + timeTriggers.remove(tw); + + if (applyMisfire(tw)) { + if (tw.trigger.getNextFireTime() != null) { + timeTriggers.add(tw); + } + tw = null; + continue; + } + + if(tw.trigger.getNextFireTime().getTime() > noLaterThan) { + timeTriggers.add(tw); + return null; + } + + tw.state = TriggerWrapper.STATE_ACQUIRED; + + tw.trigger.setFireInstanceId(getFiredTriggerRecordId()); + Trigger trig = (Trigger) tw.trigger.clone(); + return trig; + } + } + + return null; + } + + /** + *
+ * Inform the JobStore
that the scheduler no longer plans to
+ * fire the given Trigger
, that it had previously acquired
+ * (reserved).
+ *
+ * Inform the JobStore
that the scheduler is now firing the
+ * given Trigger
(executing its associated Job
),
+ * that it had previously acquired (reserved).
+ *
+ * Inform the JobStore
that the scheduler has completed the
+ * firing of the given Trigger
(and the execution its
+ * associated Job
), and that the {@link com.fr.third.org.quartz.JobDataMap}
+ * in the given JobDetail
should be updated if the Job
+ * is stateful.
+ *
ClassLoadHelper
that simply calls Class.forName(..)
.
+ *
+ * @see com.fr.third.org.quartz.spi.ClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.ThreadContextClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.CascadingClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.LoadingLoaderClassLoadHelper
+ *
+ * @author jhouse
+ * @author pl47ypus
+ */
+public class SimpleClassLoadHelper implements ClassLoadHelper {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Called to give the ClassLoadHelper a chance to initialize itself,
+ * including the opportunity to "steal" the class loader off of the calling
+ * thread, which is the thread that is initializing Quartz.
+ */
+ public void initialize() {
+ }
+
+ /**
+ * Return the class with the given name.
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return Class.forName(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.net.URL object
+ */
+ public URL getResource(String name) {
+ return getClassLoader().getResource(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.io.InputStream object
+ */
+ public InputStream getResourceAsStream(String name) {
+ return getClassLoader().getResourceAsStream(name);
+ }
+
+ /**
+ * Enable sharing of the class-loader with 3rd party (e.g. digester).
+ *
+ * @return the class-loader user be the helper.
+ */
+ public ClassLoader getClassLoader() {
+ // To follow the same behavior of Class.forName(...) I had to play
+ // dirty (Supported by Sun, IBM & BEA JVMs)
+ // ToDo - Test it more.
+ try {
+ // Get a reference to this class' class-loader
+ ClassLoader cl = this.getClass().getClassLoader();
+ // Create a method instance representing the protected
+ // getCallerClassLoader method of class ClassLoader
+ Method mthd = ClassLoader.class.getDeclaredMethod(
+ "getCallerClassLoader", new Class[0]);
+ // Make the method accessible.
+ AccessibleObject.setAccessible(new AccessibleObject[] {mthd}, true);
+ // Try to get the caller's class-loader
+ return (ClassLoader)mthd.invoke(cl, new Object[0]);
+ } catch (Exception all) {
+ // Use this class' class-loader
+ return this.getClass().getClassLoader();
+ }
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleClassLoadHelper.java.bak
new file mode 100644
index 000000000..309a0ff0b
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleClassLoadHelper.java.bak
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.simpl;
+
+import org.quartz.spi.ClassLoadHelper;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.io.InputStream;
+
+/**
+ * A ClassLoadHelper
that simply calls Class.forName(..)
.
+ *
+ * @see org.quartz.spi.ClassLoadHelper
+ * @see org.quartz.simpl.ThreadContextClassLoadHelper
+ * @see org.quartz.simpl.CascadingClassLoadHelper
+ * @see org.quartz.simpl.LoadingLoaderClassLoadHelper
+ *
+ * @author jhouse
+ */
+public class SimpleClassLoadHelper implements ClassLoadHelper {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Called to give the ClassLoadHelper a chance to initialize itself,
+ * including the oportunity to "steal" the class loader off of the calling
+ * thread, which is the thread that is initializing Quartz.
+ */
+ public void initialize() {
+ }
+
+ /**
+ * Return the class with the given name.
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return Class.forName(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.net.URL object
+ */
+ public URL getResource(String name) {
+ return getClassLoader().getResource(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.io.InputStream object
+ */
+ public InputStream getResourceAsStream(String name) {
+ return getClassLoader().getResourceAsStream(name);
+ }
+
+ private ClassLoader getClassLoader() {
+ // To follow the same behavior of Class.forName(...) I had to play
+ // dirty (Supported by Sun, IBM & BEA JVMs)
+ // ToDo - Test it more.
+ try {
+ // Get a reference to this class' class-loader
+ ClassLoader cl = this.getClass().getClassLoader();
+ // Create a method instance represnting the protected
+ // getCallerClassLoader method of class ClassLoader
+ Method mthd = ClassLoader.class.getDeclaredMethod(
+ "getCallerClassLoader", new Class[0]);
+ // Make the method accessible.
+ AccessibleObject.setAccessible(new AccessibleObject[] {mthd}, true);
+ // Try to get the caller's class-loader
+ return (ClassLoader)mthd.invoke(cl, new Object[0]);
+ } catch (Exception all) {
+ // Use this class' class-loader
+ return this.getClass().getClassLoader();
+ }
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleInstanceIdGenerator.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleInstanceIdGenerator.java
new file mode 100644
index 000000000..a64e924a0
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleInstanceIdGenerator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package com.fr.third.org.quartz.simpl;
+
+import java.net.InetAddress;
+
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.spi.InstanceIdGenerator;
+
+/**
+ * The default InstanceIdGenerator used by Quartz when instance id is to be
+ * automatically generated. Instance id is of the form HOSTNAME + CURRENT_TIME.
+ *
+ * @see InstanceIdGenerator
+ * @see HostnameInstanceIdGenerator
+ */
+public class SimpleInstanceIdGenerator implements InstanceIdGenerator {
+ public String generateInstanceId() throws SchedulerException {
+ try {
+ return InetAddress.getLocalHost().getHostName() + System.currentTimeMillis();
+ } catch (Exception e) {
+ throw new SchedulerException("Couldn't get host name!", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleJobFactory.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleJobFactory.java
new file mode 100644
index 000000000..f3c81ff8e
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleJobFactory.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package com.fr.third.org.quartz.simpl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.Job;
+import com.fr.third.org.quartz.JobDetail;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.spi.JobFactory;
+import com.fr.third.org.quartz.spi.TriggerFiredBundle;
+
+/**
+ * The default JobFactory used by Quartz - simply calls
+ * newInstance()
on the job class.
+ *
+ * @see JobFactory
+ * @see PropertySettingJobFactory
+ *
+ * @author jhouse
+ */
+public class SimpleJobFactory implements JobFactory {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ protected Log getLog() {
+ return log;
+ }
+
+ public Job newJob(TriggerFiredBundle bundle) throws SchedulerException {
+
+ JobDetail jobDetail = bundle.getJobDetail();
+ Class jobClass = jobDetail.getJobClass();
+ try {
+ if(log.isDebugEnabled()) {
+ log.debug(
+ "Producing instance of Job '" + jobDetail.getFullName() +
+ "', class=" + jobClass.getName());
+ }
+
+ return (Job) jobClass.newInstance();
+ } catch (Exception e) {
+ SchedulerException se = new SchedulerException(
+ "Problem instantiating class '"
+ + jobDetail.getJobClass().getName() + "'", e);
+ throw se;
+ }
+ }
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleThreadPool.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleThreadPool.java
new file mode 100644
index 000000000..d7f0befff
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleThreadPool.java
@@ -0,0 +1,576 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.simpl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.SchedulerConfigException;
+import com.fr.third.org.quartz.spi.ThreadPool;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ * This is class is a simple implementation of a thread pool, based on the
+ * {@link com.fr.third.org.quartz.spi.ThreadPool}
interface.
+ *
+ * Runnable
objects are sent to the pool with the {@link #runInThread(Runnable)}
+ * method, which blocks until a Thread
becomes available.
+ *
+ * The pool has a fixed number of Thread
s, and does not grow or
+ * shrink based on demand.
+ *
+ * Create a new (unconfigured) SimpleThreadPool
.
+ *
+ * Create a new SimpleThreadPool
with the specified number
+ * of Thread
s that have the given priority.
+ *
Threads
in the pool, must
+ * be > 0.
+ * @param threadPriority
+ * the thread priority for the worker threads.
+ *
+ * @see java.lang.Thread
+ */
+ public SimpleThreadPool(int threadCount, int threadPriority) {
+ setThreadCount(threadCount);
+ setThreadPriority(threadPriority);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public Log getLog() {
+ return log;
+ }
+
+ public int getPoolSize() {
+ return getThreadCount();
+ }
+
+ /**
+ *
+ * Set the number of worker threads in the pool - has no effect after
+ * initialize()
has been called.
+ *
+ * Get the number of worker threads in the pool. + *
+ */ + public int getThreadCount() { + return count; + } + + /** + *
+ * Set the thread priority of worker threads in the pool - has no effect
+ * after initialize()
has been called.
+ *
+ * Get the thread priority of worker threads in the pool. + *
+ */ + public int getThreadPriority() { + return prio; + } + + public void setThreadNamePrefix(String prfx) { + this.threadNamePrefix = prfx; + } + + public String getThreadNamePrefix() { + return threadNamePrefix; + } + + /** + * @return Returns the + * threadsInheritContextClassLoaderOfInitializingThread. + */ + public boolean isThreadsInheritContextClassLoaderOfInitializingThread() { + return inheritLoader; + } + + /** + * @param inheritLoader + * The threadsInheritContextClassLoaderOfInitializingThread to + * set. + */ + public void setThreadsInheritContextClassLoaderOfInitializingThread( + boolean inheritLoader) { + this.inheritLoader = inheritLoader; + } + + public boolean isThreadsInheritGroupOfInitializingThread() { + return inheritGroup; + } + + public void setThreadsInheritGroupOfInitializingThread( + boolean inheritGroup) { + this.inheritGroup = inheritGroup; + } + + + /** + * @return Returns the value of makeThreadsDaemons. + */ + public boolean isMakeThreadsDaemons() { + return makeThreadsDaemons; + } + + /** + * @param makeThreadsDaemons + * The value of makeThreadsDaemons to set. + */ + public void setMakeThreadsDaemons(boolean makeThreadsDaemons) { + this.makeThreadsDaemons = makeThreadsDaemons; + } + + public void initialize() throws SchedulerConfigException { + + if (count <= 0) { + throw new SchedulerConfigException( + "Thread count must be > 0"); + } + if (prio <= 0 || prio > 9) { + throw new SchedulerConfigException( + "Thread priority must be > 0 and <= 9"); + } + + if(isThreadsInheritGroupOfInitializingThread()) { + threadGroup = Thread.currentThread().getThreadGroup(); + } else { + // follow the threadGroup tree to the root thread group. + threadGroup = Thread.currentThread().getThreadGroup(); + ThreadGroup parent = threadGroup; + while ( !parent.getName().equals("main") ) { + threadGroup = parent; + parent = threadGroup.getParent(); + } + threadGroup = new ThreadGroup(parent, "SimpleThreadPool"); + if (isMakeThreadsDaemons()) { + threadGroup.setDaemon(true); + } + } + + + if (isThreadsInheritContextClassLoaderOfInitializingThread()) { + getLog().info( + "Job execution threads will use class loader of thread: " + + Thread.currentThread().getName()); + } + + // create the worker threads and start them + Iterator workerThreads = createWorkerThreads(count).iterator(); + while(workerThreads.hasNext()) { + WorkerThread wt = (WorkerThread) workerThreads.next(); + wt.start(); + availWorkers.add(wt); + } + } + + protected List createWorkerThreads(int count) { + workers = new LinkedList(); + for (int i = 1; i<= count; ++i) { + WorkerThread wt = new WorkerThread(this, threadGroup, + getThreadNamePrefix() + "-" + i, + getThreadPriority(), + isMakeThreadsDaemons()); + if (isThreadsInheritContextClassLoaderOfInitializingThread()) { + wt.setContextClassLoader(Thread.currentThread() + .getContextClassLoader()); + } + workers.add(wt); + } + + return workers; + } + + /** + *+ * Terminate any worker threads in this thread group. + *
+ * + *+ * Jobs currently in progress will complete. + *
+ */ + public void shutdown() { + shutdown(true); + } + + /** + *+ * Terminate any worker threads in this thread group. + *
+ * + *+ * Jobs currently in progress will complete. + *
+ */ + public void shutdown(boolean waitForJobsToComplete) { + + synchronized (nextRunnableLock) { + isShutdown = true; + + // signal each worker thread to shut down + Iterator workerThreads = workers.iterator(); + while(workerThreads.hasNext()) { + WorkerThread wt = (WorkerThread) workerThreads.next(); + wt.shutdown(); + availWorkers.remove(wt); + } + + // Give waiting (wait(1000)) worker threads a chance to shut down. + // Active worker threads will shut down after finishing their + // current job. + nextRunnableLock.notifyAll(); + + if (waitForJobsToComplete == true) { + + // wait for hand-off in runInThread to complete... + while(handoffPending) { + try { nextRunnableLock.wait(100); } catch(Throwable t) {} + } + + // Wait until all worker threads are shut down + while (busyWorkers.size() > 0) { + WorkerThread wt = (WorkerThread) busyWorkers.getFirst(); + try { + getLog().debug( + "Waiting for thread " + wt.getName() + + " to shut down"); + + // note: with waiting infinite time the + // application may appear to 'hang'. + nextRunnableLock.wait(2000); + } catch (InterruptedException ex) { + } + } + + getLog().debug("shutdown complete"); + } + } + } + + /** + *
+ * Run the given Runnable
object in the next available
+ * Thread
. If while waiting the thread pool is asked to
+ * shut down, the Runnable is executed immediately within a new additional
+ * thread.
+ *
Runnable
to be added.
+ */
+ public boolean runInThread(Runnable runnable) {
+ if (runnable == null) {
+ return false;
+ }
+
+ synchronized (nextRunnableLock) {
+
+ handoffPending = true;
+
+ // Wait until a worker thread is available
+ while ((availWorkers.size() < 1) && !isShutdown) {
+ try {
+ nextRunnableLock.wait(500);
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ if (!isShutdown) {
+ WorkerThread wt = (WorkerThread)availWorkers.removeFirst();
+ busyWorkers.add(wt);
+ wt.run(runnable);
+ } else {
+ // If the thread pool is going down, execute the Runnable
+ // within a new additional worker thread (no thread from the pool).
+ WorkerThread wt = new WorkerThread(this, threadGroup,
+ "WorkerThread-LastJob", prio, isMakeThreadsDaemons(), runnable);
+ busyWorkers.add(wt);
+ workers.add(wt);
+ wt.start();
+ }
+ nextRunnableLock.notifyAll();
+ handoffPending = false;
+ }
+
+ return true;
+ }
+
+ public int blockForAvailableThreads() {
+ synchronized(nextRunnableLock) {
+
+ while((availWorkers.size() < 1 || handoffPending) && !isShutdown) {
+ try {
+ nextRunnableLock.wait(500);
+ } catch (InterruptedException ignore) {
+ }
+ }
+
+ return availWorkers.size();
+ }
+ }
+
+ protected void makeAvailable(WorkerThread wt) {
+ synchronized(nextRunnableLock) {
+ if(!isShutdown) {
+ availWorkers.add(wt);
+ }
+ busyWorkers.remove(wt);
+ nextRunnableLock.notifyAll();
+ }
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * WorkerThread Class.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * + * A Worker loops, waiting to execute tasks. + *
+ */ + class WorkerThread extends Thread { + + // A flag that signals the WorkerThread to terminate. + private boolean run = true; + + private SimpleThreadPool tp; + + private Runnable runnable = null; + + /** + *+ * Create a worker thread and start it. Waiting for the next Runnable, + * executing it, and waiting for the next Runnable, until the shutdown + * flag is set. + *
+ */ + WorkerThread(SimpleThreadPool tp, ThreadGroup threadGroup, String name, + int prio, boolean isDaemon) { + + this(tp, threadGroup, name, prio, isDaemon, null); + } + + /** + *+ * Create a worker thread, start it, execute the runnable and terminate + * the thread (one time execution). + *
+ */ + WorkerThread(SimpleThreadPool tp, ThreadGroup threadGroup, String name, + int prio, boolean isDaemon, Runnable runnable) { + + super(threadGroup, name); + this.tp = tp; + this.runnable = runnable; + setPriority(prio); + setDaemon(isDaemon); + } + + /** + *+ * Signal the thread that it should terminate. + *
+ */ + void shutdown() { + synchronized (this) { + run = false; + } + } + + public void run(Runnable newRunnable) { + synchronized(this) { + if(runnable != null) { + throw new IllegalStateException("Already running a Runnable!"); + } + + runnable = newRunnable; + this.notifyAll(); + } + } + + /** + *+ * Loop, executing targets as they are received. + *
+ */ + public void run() { + boolean ran = false; + boolean runOnce = false; + boolean shouldRun = false; + synchronized(this) { + runOnce = (runnable != null); + shouldRun = run; + } + + while (shouldRun) { + try { + synchronized(this) { + while (runnable == null && run) { + this.wait(500); + } + } + + if (runnable != null) { + ran = true; + runnable.run(); + } + } catch (InterruptedException unblock) { + // do nothing (loop will terminate if shutdown() was called + try { + getLog().error("worker threat got 'interrupt'ed.", unblock); + } catch(Exception e) { + // ignore to help with a tomcat glitch + } + } catch (Exception exceptionInRunnable) { + try { + getLog().error("Error while executing the Runnable: ", + exceptionInRunnable); + } catch(Exception e) { + // ignore to help with a tomcat glitch + } + } finally { + synchronized(this) { + runnable = null; + } + // repair the thread in case the runnable mucked it up... + if(getPriority() != tp.getThreadPriority()) { + setPriority(tp.getThreadPriority()); + } + + if (runOnce) { + synchronized(this) { + run = false; + } + } else if(ran) { + ran = false; + makeAvailable(this); + } + + } + + // read value of run within synchronized block to be + // sure of its value + synchronized(this) { + shouldRun = run; + } + } + + //if (log.isDebugEnabled()) + try { + getLog().debug("WorkerThread is shutting down"); + } catch(Exception e) { + // ignore to help with a tomcat glitch + } + } + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleTimeBroker.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleTimeBroker.java new file mode 100644 index 000000000..a9e811387 --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/SimpleTimeBroker.java @@ -0,0 +1,76 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.simpl; + +import java.util.Date; + +import com.fr.third.org.quartz.SchedulerConfigException; +import com.fr.third.org.quartz.spi.TimeBroker; + +/** + *
+ * The interface to be implemented by classes that want to provide a mechanism
+ * by which the {@link com.fr.third.org.quartz.core.QuartzScheduler}
can
+ * reliably determine the current time.
+ *
+ * In general, the default implementation of this interface ({@link com.fr.third.org.quartz.simpl.SimpleTimeBroker}
-
+ * which simply uses System.getCurrentTimeMillis()
)is
+ * sufficient. However situations may exist where this default scheme is
+ * lacking in its robustsness - especially when Quartz is used in a clustered
+ * configuration. For example, if one or more of the machines in the cluster
+ * has a system time that varies by more than a few seconds from the clocks on
+ * the other systems in the cluster, scheduling confusion will result.
+ *
+ * Get the current time, simply using new Date()
.
+ *
ClassLoadHelper
that uses either the current thread's
+ * context class loader (Thread.currentThread().getContextClassLoader().loadClass( .. )
).
+ *
+ * @see com.fr.third.org.quartz.spi.ClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.InitThreadContextClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.SimpleClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.CascadingClassLoadHelper
+ * @see com.fr.third.org.quartz.simpl.LoadingLoaderClassLoadHelper
+ *
+ * @author jhouse
+ * @author pl47ypus
+ */
+public class ThreadContextClassLoadHelper implements ClassLoadHelper {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Called to give the ClassLoadHelper a chance to initialize itself,
+ * including the opportunity to "steal" the class loader off of the calling
+ * thread, which is the thread that is initializing Quartz.
+ */
+ public void initialize() {
+ }
+
+ /**
+ * Return the class with the given name.
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return getClassLoader().loadClass(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.net.URL object
+ */
+ public URL getResource(String name) {
+ return getClassLoader().getResource(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.io.InputStream object
+ */
+ public InputStream getResourceAsStream(String name) {
+ return getClassLoader().getResourceAsStream(name);
+ }
+
+ /**
+ * Enable sharing of the class-loader with 3rd party (e.g. digester).
+ *
+ * @return the class-loader user be the helper.
+ */
+ public ClassLoader getClassLoader() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/ThreadContextClassLoadHelper.java.bak b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/ThreadContextClassLoadHelper.java.bak
new file mode 100644
index 000000000..80a9a3f7d
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/ThreadContextClassLoadHelper.java.bak
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.simpl;
+
+import org.quartz.spi.ClassLoadHelper;
+
+import java.net.URL;
+import java.io.InputStream;
+
+/**
+ * A ClassLoadHelper
that uses either the current thread's
+ * context class loader (Thread.currentThread().getContextClassLoader().loadClass( .. )
).
+ *
+ * @see org.quartz.spi.ClassLoadHelper
+ * @see org.quartz.simpl.InitThreadContextClassLoadHelper
+ * @see org.quartz.simpl.SimpleClassLoadHelper
+ * @see org.quartz.simpl.CascadingClassLoadHelper
+ * @see org.quartz.simpl.LoadingLoaderClassLoadHelper
+ *
+ * @author jhouse
+ */
+public class ThreadContextClassLoadHelper implements ClassLoadHelper {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Called to give the ClassLoadHelper a chance to initialize itself,
+ * including the oportunity to "steal" the class loader off of the calling
+ * thread, which is the thread that is initializing Quartz.
+ */
+ public void initialize() {
+ }
+
+ /**
+ * Return the class with the given name.
+ */
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return getClassLoader().loadClass(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.net.URL object
+ */
+ public URL getResource(String name) {
+ return getClassLoader().getResource(name);
+ }
+
+ /**
+ * Finds a resource with a given name. This method returns null if no
+ * resource with this name is found.
+ * @param name name of the desired resource
+ * @return a java.io.InputStream object
+ */
+ public InputStream getResourceAsStream(String name) {
+ return getClassLoader().getResourceAsStream(name);
+ }
+
+
+ private ClassLoader getClassLoader() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/ZeroSizeThreadPool.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/ZeroSizeThreadPool.java
new file mode 100644
index 000000000..ed11a20fa
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/simpl/ZeroSizeThreadPool.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.simpl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import com.fr.third.org.quartz.SchedulerConfigException;
+import com.fr.third.org.quartz.spi.ThreadPool;
+
+/**
+ *
+ * This is class is a simple implementation of a zero size thread pool, based on the
+ * {@link com.fr.third.org.quartz.spi.ThreadPool}
interface.
+ *
+ * The pool has zero Thread
s and does not grow or shrink based on demand.
+ * Which means it is obviously not useful for most scenarios. When it may be useful
+ * is to prevent creating any worker threads at all - which may be desirable for
+ * the sole purpose of preserving system resources in the case where the scheduler
+ * instance only exists in order to schedule jobs, but which will never execute
+ * jobs (e.g. will never have start() called on it).
+ *
+ *
+ * + * @author Wayne Fay + */ +public class ZeroSizeThreadPool implements ThreadPool { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private final Log log = LogFactory.getLog(getClass()); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *
+ * Create a new ZeroSizeThreadPool
.
+ *
Contains simple / light-weight implementations (with no dependencies on +external libraries) of interfaces required by the +com.fr.third.org.quartz.core.QuartzScheduler.
+ +
+ * An InstanceIdGenerator is responsible for generating the clusterwide unique
+ * instance id for a Scheduler
node.
+ *
+ * This interface may be of use to those wishing to have specific control over
+ * the mechanism by which the Scheduler
instances in their
+ * application are named.
+ *
Scheduler
+ *
+ * @return The clusterwide unique instance id.
+ */
+ String generateInstanceId() throws SchedulerException;
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/JobFactory.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/JobFactory.java
new file mode 100644
index 000000000..97dd71501
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/JobFactory.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package com.fr.third.org.quartz.spi;
+
+import com.fr.third.org.quartz.Job;
+import com.fr.third.org.quartz.SchedulerException;
+
+/**
+ *
+ * A JobFactory is responsible for producing instances of Job
+ * classes.
+ *
+ * This interface may be of use to those wishing to have their application
+ * produce Job
instances via some special mechanism, such as to
+ * give the opertunity for dependency injection.
+ *
Job
instance on which to call execute.
+ *
+ *
+ * It should be extremely rare for this method to throw an exception -
+ * basically only the the case where there is no way at all to instantiate
+ * and prepare the Job for execution. When the exception is thrown, the
+ * Scheduler will move all triggers associated with the Job into the
+ * Trigger.STATE_ERROR
state, which will require human
+ * intervention (e.g. an application restart after fixing whatever
+ * configuration problem led to the issue wih instantiating the Job.
+ *
JobDetail
+ * and other info relating to the trigger firing can be obtained.
+ * @throws SchedulerException if there is a problem instantiating the Job.
+ * @return the newly instantiated Job
+ */
+ Job newJob(TriggerFiredBundle bundle) throws SchedulerException;
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/JobStore.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/JobStore.java
new file mode 100644
index 000000000..a33de56f3
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/JobStore.java
@@ -0,0 +1,664 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.spi;
+
+import java.util.Set;
+
+import com.fr.third.org.quartz.Calendar;
+import com.fr.third.org.quartz.JobDetail;
+import com.fr.third.org.quartz.JobPersistenceException;
+import com.fr.third.org.quartz.ObjectAlreadyExistsException;
+import com.fr.third.org.quartz.SchedulerConfigException;
+import com.fr.third.org.quartz.SchedulerException;
+import com.fr.third.org.quartz.Trigger;
+import com.fr.third.org.quartz.core.SchedulingContext;
+
+/**
+ *
+ * The interface to be implemented by classes that want to provide a {@link com.fr.third.org.quartz.Job}
+ * and {@link com.fr.third.org.quartz.Trigger}
storage mechanism for the
+ * {@link com.fr.third.org.quartz.core.QuartzScheduler}
's use.
+ *
+ * Storage of Job
s and Trigger
s should be keyed
+ * on the combination of their name and group for uniqueness.
+ *
+ * Called by the QuartzScheduler before the JobStore
is
+ * used, in order to give the it a chance to initialize.
+ *
+ * Called by the QuartzScheduler to inform the JobStore
that
+ * the scheduler has started.
+ *
+ * Called by the QuartzScheduler to inform the JobStore
that
+ * it should free up all of it's resources because the scheduler is
+ * shutting down.
+ *
+ * Store the given {@link com.fr.third.org.quartz.JobDetail}
and {@link com.fr.third.org.quartz.Trigger}
.
+ *
JobDetail
to be stored.
+ * @param newTrigger
+ * The Trigger
to be stored.
+ * @throws ObjectAlreadyExistsException
+ * if a Job
with the same name/group already
+ * exists.
+ */
+ void storeJobAndTrigger(SchedulingContext ctxt, JobDetail newJob,
+ Trigger newTrigger) throws ObjectAlreadyExistsException,
+ JobPersistenceException;
+
+ /**
+ *
+ * Store the given {@link com.fr.third.org.quartz.JobDetail}
.
+ *
JobDetail
to be stored.
+ * @param replaceExisting
+ * If true
, any Job
existing in the
+ * JobStore
with the same name & group should be
+ * over-written.
+ * @throws ObjectAlreadyExistsException
+ * if a Job
with the same name/group already
+ * exists, and replaceExisting is set to false.
+ */
+ void storeJob(SchedulingContext ctxt, JobDetail newJob,
+ boolean replaceExisting) throws ObjectAlreadyExistsException,
+ JobPersistenceException;
+
+ /**
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Job}
with the given
+ * name, and any {@link com.fr.third.org.quartz.Trigger}
s that reference
+ * it.
+ *
+ * If removal of the Job
results in an empty group, the
+ * group should be removed from the JobStore
's list of
+ * known group names.
+ *
Job
to be removed.
+ * @param groupName
+ * The group name of the Job
to be removed.
+ * @return true
if a Job
with the given name &
+ * group was found and removed from the store.
+ */
+ boolean removeJob(SchedulingContext ctxt, String jobName,
+ String groupName) throws JobPersistenceException;
+
+ /**
+ *
+ * Retrieve the {@link com.fr.third.org.quartz.JobDetail}
for the given
+ * {@link com.fr.third.org.quartz.Job}
.
+ *
Job
to be retrieved.
+ * @param groupName
+ * The group name of the Job
to be retrieved.
+ * @return The desired Job
, or null if there is no match.
+ */
+ JobDetail retrieveJob(SchedulingContext ctxt, String jobName,
+ String groupName) throws JobPersistenceException;
+
+ /**
+ *
+ * Store the given {@link com.fr.third.org.quartz.Trigger}
.
+ *
Trigger
to be stored.
+ * @param replaceExisting
+ * If true
, any Trigger
existing in
+ * the JobStore
with the same name & group should
+ * be over-written.
+ * @throws ObjectAlreadyExistsException
+ * if a Trigger
with the same name/group already
+ * exists, and replaceExisting is set to false.
+ *
+ * @see #pauseTriggerGroup(SchedulingContext, String)
+ */
+ void storeTrigger(SchedulingContext ctxt, Trigger newTrigger,
+ boolean replaceExisting) throws ObjectAlreadyExistsException,
+ JobPersistenceException;
+
+ /**
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Trigger}
with the
+ * given name.
+ *
+ * If removal of the Trigger
results in an empty group, the
+ * group should be removed from the JobStore
's list of
+ * known group names.
+ *
+ * If removal of the Trigger
results in an 'orphaned' Job
+ * that is not 'durable', then the Job
should be deleted
+ * also.
+ *
Trigger
to be removed.
+ * @param groupName
+ * The group name of the Trigger
to be removed.
+ * @return true
if a Trigger
with the given
+ * name & group was found and removed from the store.
+ */
+ boolean removeTrigger(SchedulingContext ctxt, String triggerName,
+ String groupName) throws JobPersistenceException;
+
+ /**
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Trigger}
with the
+ * given name, and store the new given one - which must be associated
+ * with the same job.
+ *
Trigger
to be removed.
+ * @param groupName
+ * The group name of the Trigger
to be removed.
+ * @param newTrigger
+ * The new Trigger
to be stored.
+ * @return true
if a Trigger
with the given
+ * name & group was found and removed from the store.
+ */
+ boolean replaceTrigger(SchedulingContext ctxt, String triggerName,
+ String groupName, Trigger newTrigger) throws JobPersistenceException;
+
+
+ /**
+ *
+ * Retrieve the given {@link com.fr.third.org.quartz.Trigger}
.
+ *
Trigger
to be retrieved.
+ * @param groupName
+ * The group name of the Trigger
to be retrieved.
+ * @return The desired Trigger
, or null if there is no
+ * match.
+ */
+ Trigger retrieveTrigger(SchedulingContext ctxt, String triggerName,
+ String groupName) throws JobPersistenceException;
+
+ /**
+ *
+ * Store the given {@link com.fr.third.org.quartz.Calendar}
.
+ *
Calendar
to be stored.
+ * @param replaceExisting
+ * If true
, any Calendar
existing
+ * in the JobStore
with the same name & group
+ * should be over-written.
+ * @param updateTriggers
+ * If true
, any Trigger
s existing
+ * in the JobStore
that reference an existing
+ * Calendar with the same name with have their next fire time
+ * re-computed with the new Calendar
.
+ * @throws ObjectAlreadyExistsException
+ * if a Calendar
with the same name already
+ * exists, and replaceExisting is set to false.
+ */
+ void storeCalendar(SchedulingContext ctxt, String name,
+ Calendar calendar, boolean replaceExisting, boolean updateTriggers)
+ throws ObjectAlreadyExistsException, JobPersistenceException;
+
+ /**
+ *
+ * Remove (delete) the {@link com.fr.third.org.quartz.Calendar}
with the
+ * given name.
+ *
+ * If removal of the Calendar
would result in
+ * JobPersistenceException
will be thrown.
Calendar
to be removed.
+ * @return true
if a Calendar
with the given name
+ * was found and removed from the store.
+ */
+ boolean removeCalendar(SchedulingContext ctxt, String calName)
+ throws JobPersistenceException;
+
+ /**
+ *
+ * Retrieve the given {@link com.fr.third.org.quartz.Trigger}
.
+ *
Calendar
to be retrieved.
+ * @return The desired Calendar
, or null if there is no
+ * match.
+ */
+ Calendar retrieveCalendar(SchedulingContext ctxt, String calName)
+ throws JobPersistenceException;
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Informational methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ *
+ * Get the number of {@link com.fr.third.org.quartz.Job}
s that are
+ * stored in the JobsStore
.
+ *
+ * Get the number of {@link com.fr.third.org.quartz.Trigger}
s that are
+ * stored in the JobsStore
.
+ *
+ * Get the number of {@link com.fr.third.org.quartz.Calendar}
s that are
+ * stored in the JobsStore
.
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Job}
s that
+ * have the given group name.
+ *
+ * If there are no jobs in the given group name, the result should be a
+ * zero-length array (not null
).
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Trigger}
s
+ * that have the given group name.
+ *
+ * If there are no triggers in the given group name, the result should be a
+ * zero-length array (not null
).
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Job}
+ * groups.
+ *
+ * If there are no known group names, the result should be a zero-length
+ * array (not null
).
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Trigger}
+ * groups.
+ *
+ * If there are no known group names, the result should be a zero-length
+ * array (not null
).
+ *
+ * Get the names of all of the {@link com.fr.third.org.quartz.Calendar}
s
+ * in the JobStore
.
+ *
+ * If there are no Calendars in the given group name, the result should be
+ * a zero-length array (not null
).
+ *
+ * Get all of the Triggers that are associated to the given Job. + *
+ * + *+ * If there are no matches, a zero-length array should be returned. + *
+ */ + Trigger[] getTriggersForJob(SchedulingContext ctxt, String jobName, + String groupName) throws JobPersistenceException; + + /** + *
+ * Get the current state of the identified {@link Trigger}
.
+ *
+ * Pause the {@link com.fr.third.org.quartz.Trigger}
with the given name.
+ *
+ * Pause all of the {@link com.fr.third.org.quartz.Trigger}s
in the
+ * given group.
+ *
+ * The JobStore should "remember" that the group is paused, and impose the + * pause on any new triggers that are added to the group while the group is + * paused. + *
+ * + * @see #resumeTriggerGroup(SchedulingContext, String) + */ + void pauseTriggerGroup(SchedulingContext ctxt, String groupName) + throws JobPersistenceException; + + /** + *
+ * Pause the {@link com.fr.third.org.quartz.Job}
with the given name - by
+ * pausing all of its current Trigger
s.
+ *
+ * Pause all of the {@link com.fr.third.org.quartz.Job}s
in the given
+ * group - by pausing all of their Trigger
s.
+ *
+ * The JobStore should "remember" that the group is paused, and impose the + * pause on any new jobs that are added to the group while the group is + * paused. + *
+ * + * @see #resumeJobGroup(SchedulingContext, String) + */ + void pauseJobGroup(SchedulingContext ctxt, String groupName) + throws JobPersistenceException; + + /** + *
+ * Resume (un-pause) the {@link com.fr.third.org.quartz.Trigger}
with the
+ * given name.
+ *
+ * If the Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link com.fr.third.org.quartz.Trigger}s
+ * in the given group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Resume (un-pause) the {@link com.fr.third.org.quartz.Job}
with the
+ * given name.
+ *
+ * If any of the Job
'sTrigger
s missed one
+ * or more fire-times, then the Trigger
's misfire
+ * instruction will be applied.
+ *
+ * Resume (un-pause) all of the {@link com.fr.third.org.quartz.Job}s
in
+ * the given group.
+ *
+ * If any of the Job
s had Trigger
s that
+ * missed one or more fire-times, then the Trigger
's
+ * misfire instruction will be applied.
+ *
+ * Pause all triggers - equivalent of calling pauseTriggerGroup(group)
+ * on every group.
+ *
+ * When resumeAll()
is called (to un-pause), trigger misfire
+ * instructions WILL be applied.
+ *
+ * Resume (un-pause) all triggers - equivalent of calling resumeTriggerGroup(group)
+ * on every group.
+ *
+ * If any Trigger
missed one or more fire-times, then the
+ * Trigger
's misfire instruction will be applied.
+ *
+ * Get a handle to the next trigger to be fired, and mark it as 'reserved' + * by the calling scheduler. + *
+ * + * @param noLaterThan If > 0, the JobStore should only return a Trigger + * that will fire no later than the time represented in this value as + * milliseconds. + * @see #releaseAcquiredTrigger(SchedulingContext, Trigger) + */ + Trigger acquireNextTrigger(SchedulingContext ctxt, long noLaterThan) + throws JobPersistenceException; + + /** + *
+ * Inform the JobStore
that the scheduler no longer plans to
+ * fire the given Trigger
, that it had previously acquired
+ * (reserved).
+ *
+ * Inform the JobStore
that the scheduler is now firing the
+ * given Trigger
(executing its associated Job
),
+ * that it had previously acquired (reserved).
+ *
+ * Inform the JobStore
that the scheduler has completed the
+ * firing of the given Trigger
(and the execution of its
+ * associated Job
completed, threw an exception, or was vetoed),
+ * and that the {@link com.fr.third.org.quartz.JobDataMap}
+ * in the given JobDetail
should be updated if the Job
+ * is stateful.
+ *
+ * Provides an interface for a class to become a "plugin" to Quartz. + *
+ * + *
+ * Plugins can do virtually anything you wish, though the most interesting ones
+ * will obviously interact with the scheduler in some way - either actively: by
+ * invoking actions on the scheduler, or passively: by being a JobListener
,
+ * TriggerListener
, and/or SchedulerListener
.
+ *
+ * If you use {@link com.fr.third.org.quartz.impl.StdSchedulerFactory}
to
+ * initialize your Scheduler, it can also create and initialize your plugins -
+ * look at the configuration docs for details.
+ *
+ * If you need direct access your plugin, you can have it explicitly put a
+ * reference to itself in the Scheduler
's
+ * SchedulerContext
as part of its
+ * {@link #initialize(String, Scheduler)}
method.
+ *
+ * Called during creation of the Scheduler
in order to give
+ * the SchedulerPlugin
a chance to initialize.
+ *
+ * At this point, the Scheduler's JobStore
is not yet
+ * initialized.
+ *
+ * If you need direct access your plugin, for example during Job
+ * execution, you can have this method explicitly put a
+ * reference to this plugin in the Scheduler
's
+ * SchedulerContext
.
+ *
+ * Called when the associated Scheduler
is started, in order
+ * to let the plug-in know it can now make calls into the scheduler if it
+ * needs to.
+ *
+ * Called in order to inform the SchedulerPlugin
that it
+ * should free up all of it's resources because the scheduler is shutting
+ * down.
+ *
JobStore
instances in order to
+ * communicate signals back to the QuartzScheduler
.
+ *
+ * @author jhouse
+ */
+public interface SchedulerSignaler {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ void notifyTriggerListenersMisfired(Trigger trigger);
+
+ void notifySchedulerListenersFinalized(Trigger trigger);
+
+ void signalSchedulingChange(long candidateNewNextFireTime);
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/ThreadPool.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/ThreadPool.java
new file mode 100644
index 000000000..29718df8d
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/spi/ThreadPool.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.spi;
+
+import com.fr.third.org.quartz.SchedulerConfigException;
+
+/**
+ *
+ * The interface to be implemented by classes that want to provide a thread
+ * pool for the {@link com.fr.third.org.quartz.core.QuartzScheduler}
's use.
+ *
+ * ThreadPool
implementation instances should ideally be made
+ * for the sole use of Quartz. Most importantly, when the method
+ * blockForAvailableThreads()
returns a value of 1 or greater,
+ * there must still be at least one available thread in the pool when the
+ * method runInThread(Runnable)
is called a few moments (or
+ * many moments) later. If this assumption does not hold true, it may
+ * result in extra JobStore queries and updates, and if clustering features
+ * are being used, it may result in greater imballance of load.
+ *
+ * Execute the given {@link java.lang.Runnable}
in the next
+ * available Thread
.
+ *
+ * The implementation of this interface should not throw exceptions unless
+ * there is a serious problem (i.e. a serious misconfiguration). If there
+ * are no immediately available threads false
should be returned.
+ *
+ * Determines the number of threads that are currently available in in
+ * the pool. Useful for determining the number of times
+ * runInThread(Runnable)
can be called before returning
+ * false.
+ *
The implementation of this method should block until there is at + * least one available thread.
+ * + * @return the number of currently available threads + */ + int blockForAvailableThreads(); + + /** + *
+ * Called by the QuartzScheduler before the ThreadPool
is
+ * used, in order to give the it a chance to initialize.
+ *
+ * Called by the QuartzScheduler to inform the ThreadPool
+ * that it should free up all of it's resources because the scheduler is
+ * shutting down.
+ *
NOTE: TimeBroker is not currently used in the Quartz code base.
+ * + *
+ * The interface to be implemented by classes that want to provide a mechanism
+ * by which the {@link com.fr.third.org.quartz.core.QuartzScheduler}
can
+ * reliably determine the current time.
+ *
+ * In general, the default implementation of this interface ({@link com.fr.third.org.quartz.simpl.SimpleTimeBroker}
-
+ * which simply uses System.getCurrentTimeMillis()
)is
+ * sufficient. However situations may exist where this default scheme is
+ * lacking in its robustness - especially when Quartz is used in a clustered
+ * configuration. For example, if one or more of the machines in the cluster
+ * has a system time that varies by more than a few seconds from the clocks on
+ * the other systems in the cluster, scheduling confusion will result.
+ *
+ * Get the current time, as known by the TimeBroker
.
+ *
+ * Called by the QuartzScheduler before the TimeBroker
is
+ * used, in order to give the it a chance to initialize.
+ *
+ * Called by the QuartzScheduler to inform the TimeBroker
+ * that it should free up all of it's resources because the scheduler is
+ * shutting down.
+ *
+ * A simple class (structure) used for returning execution-time data from the
+ * JobStore to the QuartzSchedulerThread
.
+ *
Contains Service Provider Interfaces that can be implemented by those +wishing to create and use custom versions of Quartz back-end/behind-the-scenes +services.
+ +DBConnectionManager
+ * to provide connections from various sources.
+ *
+ * @see DBConnectionManager
+ * @see PoolingConnectionProvider
+ * @see JNDIConnectionProvider
+ * @see com.fr.third.org.quartz.utils.weblogic.WeblogicConnectionProvider
+ *
+ * @author Mohammad Rezaei
+ */
+public interface ConnectionProvider {
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * @return connection managed by this provider
+ * @throws SQLException
+ */
+ Connection getConnection() throws SQLException;
+
+
+ void shutdown() throws SQLException;
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/DBConnectionManager.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/DBConnectionManager.java
new file mode 100644
index 000000000..98d42d17d
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/DBConnectionManager.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.utils;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.HashMap;
+
+/**
+ * + * Manages a collection of ConnectionProviders, and provides transparent access + * to their connections. + *
+ * + * @see ConnectionProvider + * @see PoolingConnectionProvider + * @see JNDIConnectionProvider + * @see com.fr.third.org.quartz.utils.weblogic.WeblogicConnectionProvider + * + * @author James House + * @author Sharada Jambula + * @author Mohammad Rezaei + */ +public class DBConnectionManager { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public static final String DB_PROPS_PREFIX = "com.fr.third.org.quartz.db."; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private static DBConnectionManager instance = new DBConnectionManager(); + + private HashMap providers = new HashMap(); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *+ * Private constructor + *
+ * + */ + private DBConnectionManager() { + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public void addConnectionProvider(String dataSourceName, + ConnectionProvider provider) { + this.providers.put(dataSourceName, provider); + } + + /** + * Get a database connection from the DataSource with the given name. + * + * @return a database connection + * @exception SQLException + * if an error occurs, or there is no DataSource with the + * given name. + */ + public Connection getConnection(String dsName) throws SQLException { + ConnectionProvider provider = (ConnectionProvider) providers + .get(dsName); + if (provider == null) { + throw new SQLException("There is no DataSource named '" + + dsName + "'"); + } + + return provider.getConnection(); + } + + /** + * Get the class instance. + * + * @return an instance of this class + */ + public static DBConnectionManager getInstance() { + // since the instance variable is initialized at class loading time, + // it's not necessary to synchronize this method */ + return instance; + } + + /** + * Shuts down database connections from the DataSource with the given name, + * if applicable for the underlying provider. + * + * @exception SQLException + * if an error occurs, or there is no DataSource with the + * given name. + */ + public void shutdown(String dsName) throws SQLException { + + ConnectionProvider provider = (ConnectionProvider) providers + .get(dsName); + if (provider == null) { + throw new SQLException("There is no DataSource named '" + + dsName + "'"); + } + + provider.shutdown(); + + } +} diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/DirtyFlagMap.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/DirtyFlagMap.java new file mode 100644 index 000000000..85775013b --- /dev/null +++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/DirtyFlagMap.java @@ -0,0 +1,394 @@ +/* + * Copyright 2004-2005 OpenSymphony + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + */ + +/* + * Previously Copyright (c) 2001-2004 James House + */ +package com.fr.third.org.quartz.utils; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + *
+ * An implementation of Map
that wraps another Map
+ * and flags itself 'dirty' when it is modified.
+ *
+ * Create a DirtyFlagMap that 'wraps' a HashMap
.
+ *
+ * Create a DirtyFlagMap that 'wraps' a HashMap
that has the
+ * given initial capacity.
+ *
+ * Create a DirtyFlagMap that 'wraps' a HashMap
that has the
+ * given initial capacity and load factor.
+ *
+ * Clear the 'dirty' flag (set dirty flag to false
).
+ *
+ * Determine whether the Map
is flagged dirty.
+ *
+ * Get a direct handle to the underlying Map. + *
+ */ + public Map getWrappedMap() { + return map; + } + + public void clear() { + if (map.isEmpty() == false) { + dirty = true; + } + + map.clear(); + } + + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + public boolean containsValue(Object val) { + return map.containsValue(val); + } + + public Set entrySet() { + return new DirtyFlagMapEntrySet(map.entrySet()); + } + + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof DirtyFlagMap)) { + return false; + } + + return map.equals(((DirtyFlagMap) obj).getWrappedMap()); + } + + public int hashCode() + { + return map.hashCode(); + } + + public Object get(Object key) { + return map.get(key); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public Set keySet() { + return new DirtyFlagSet(map.keySet()); + } + + public Object put(Object key, Object val) { + dirty = true; + + return map.put(key, val); + } + + public void putAll(Map t) { + if (!t.isEmpty()) { + dirty = true; + } + + map.putAll(t); + } + + public Object remove(Object key) { + Object obj = map.remove(key); + + if (obj != null) { + dirty = true; + } + + return obj; + } + + public int size() { + return map.size(); + } + + public Collection values() { + return new DirtyFlagCollection(map.values()); + } + + public Object clone() { + DirtyFlagMap copy; + try { + copy = (DirtyFlagMap) super.clone(); + if (map instanceof HashMap) { + copy.map = (Map)((HashMap)map).clone(); + } + } catch (CloneNotSupportedException ex) { + throw new IncompatibleClassChangeError("Not Cloneable."); + } + + return copy; + } + + /** + * Wrap a Collection so we can mark the DirtyFlagMap as dirty if + * the underlying Collection is modified. + */ + private class DirtyFlagCollection implements Collection { + private Collection collection; + + public DirtyFlagCollection(Collection c) { + collection = c; + } + + protected Collection getWrappedCollection() { + return collection; + } + + public Iterator iterator() { + return new DirtyFlagIterator(collection.iterator()); + } + + public boolean remove(Object o) { + boolean removed = collection.remove(o); + if (removed) { + dirty = true; + } + return removed; + } + + public boolean removeAll(Collection c) { + boolean changed = collection.removeAll(c); + if (changed) { + dirty = true; + } + return changed; + } + + public boolean retainAll(Collection c) { + boolean changed = collection.retainAll(c); + if (changed) { + dirty = true; + } + return changed; + } + + public void clear() { + if (collection.isEmpty() == false) { + dirty = true; + } + collection.clear(); + } + + // Pure wrapper methods + public int size() { return collection.size(); } + public boolean isEmpty() { return collection.isEmpty(); } + public boolean contains(Object o) { return collection.contains(o); } + public boolean add(Object o) { return collection.add(o); } // Not supported + public boolean addAll(Collection c) { return collection.addAll(c); } // Not supported + public boolean containsAll(Collection c) { return collection.containsAll(c); } + public Object[] toArray() { return collection.toArray(); } + public Object[] toArray(Object[] array) { return collection.toArray(array); } + } + + /** + * Wrap a Set so we can mark the DirtyFlagMap as dirty if + * the underlying Collection is modified. + */ + private class DirtyFlagSet extends DirtyFlagCollection implements Set { + public DirtyFlagSet(Set set) { + super(set); + } + + protected Set getWrappedSet() { + return (Set)getWrappedCollection(); + } + } + + /** + * Wrap an Iterator so that we can mark the DirtyFlagMap as dirty if an + * element is removed. + */ + private class DirtyFlagIterator implements Iterator { + private Iterator iterator; + + public DirtyFlagIterator(Iterator iterator) { + this.iterator = iterator; + } + + public void remove() { + dirty = true; + iterator.remove(); + } + + // Pure wrapper methods + public boolean hasNext() { return iterator.hasNext(); } + public Object next() { return iterator.next(); } + } + + /** + * Wrap a Map.Entry Set so we can mark the Map as dirty if + * the Set is modified, and return Map.Entry objects + * wrapped in theDirtyFlagMapEntry
class.
+ */
+ private class DirtyFlagMapEntrySet extends DirtyFlagSet {
+
+ public DirtyFlagMapEntrySet(Set set) {
+ super(set);
+ }
+
+ public Iterator iterator() {
+ return new DirtyFlagMapEntryIterator(getWrappedSet().iterator());
+ }
+
+ public Object[] toArray() {
+ return toArray(new Object[super.size()]);
+ }
+
+ public Object[] toArray(Object[] array) {
+ if (array.getClass().getComponentType().isAssignableFrom(Map.Entry.class) == false) {
+ throw new IllegalArgumentException("Array must be of type assignable from Map.Entry");
+ }
+
+ int size = super.size();
+
+ Object[] result =
+ (array.length < size) ?
+ (Object[])Array.newInstance(array.getClass().getComponentType(), size) : array;
+
+ Iterator entryIter = iterator(); // Will return DirtyFlagMapEntry objects
+ for (int i = 0; i < size; i++) {
+ result[i] = entryIter.next();
+ }
+
+ if (result.length > size) {
+ result[size] = null;
+ }
+
+ return result;
+ }
+ }
+
+ /**
+ * Wrap an Iterator over Map.Entry objects so that we can
+ * mark the Map as dirty if an element is removed or modified.
+ */
+ private class DirtyFlagMapEntryIterator extends DirtyFlagIterator {
+ public DirtyFlagMapEntryIterator(Iterator iterator) {
+ super(iterator);
+ }
+
+ public Object next() {
+ return new DirtyFlagMapEntry((Map.Entry)super.next());
+ }
+ }
+
+ /**
+ * Wrap a Map.Entry so we can mark the Map as dirty if
+ * a value is set.
+ */
+ private class DirtyFlagMapEntry implements Map.Entry {
+ private Map.Entry entry;
+
+ public DirtyFlagMapEntry(Map.Entry entry) {
+ this.entry = entry;
+ }
+
+ public Object setValue(Object o) {
+ dirty = true;
+ return entry.setValue(o);
+ }
+
+ // Pure wrapper methods
+ public Object getKey() { return entry.getKey(); }
+ public Object getValue() { return entry.getValue(); }
+ }
+}
\ No newline at end of file
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/ExceptionHelper.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/ExceptionHelper.java
new file mode 100644
index 000000000..eb072a139
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/ExceptionHelper.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2004-2006 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.fr.third.org.quartz.utils;
+
+import java.lang.reflect.Method;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * + * Helper class for handling exception nesting, which is only supported in JDKs + * which are version 1.4 or later. + *
+ * + * Sample usage: + *+ * try { + * // Validate arguments + * } catch (Exception e) { + * Exception myException = new IllegalArgumentException("Doh!"); + * ExceptionHelper.setCause(myException, e); + * throw myException; + * } + *+ */ +public class ExceptionHelper { + private static Boolean supportsNestedThrowable = null; + + private ExceptionHelper() { + } + + /** + * Set the given
Throwable's cause if this JDK supports
+ * the Throwable#initCause(Throwable)
method.
+ */
+ public static Throwable setCause(Throwable exception, Throwable cause) {
+ if (exception != null) {
+ if (supportsNestedThrowable()) {
+ try {
+ Method initCauseMethod =
+ exception.getClass().getMethod("initCause", new Class[] {Throwable.class});
+ initCauseMethod.invoke(exception, new Object[] {cause});
+ } catch (Exception e) {
+ getLog().warn(
+ "Unable to invoke initCause() method on class: " +
+ exception.getClass().getName(), e);
+ }
+ }
+ }
+ return exception;
+ }
+
+ /**
+ * Get the underlying cause Throwable
of the given exception
+ * if this JDK supports the Throwable#getCause()
method.
+ */
+ public static Throwable getCause(Throwable exception) {
+ if (supportsNestedThrowable()) {
+ try {
+ Method getCauseMethod =
+ exception.getClass().getMethod("getCause", (Class[])null);
+ return (Throwable)getCauseMethod.invoke(exception, (Object[])null);
+ } catch (Exception e) {
+ getLog().warn(
+ "Unable to invoke getCause() method on class: " +
+ exception.getClass().getName(), e);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get whether the Throwable hierarchy for this JDK supports
+ * initCause()/getCause().
+ */
+ public static synchronized boolean supportsNestedThrowable() {
+ if (supportsNestedThrowable == null) {
+ try {
+ Throwable.class.getMethod("initCause", new Class[] {Throwable.class});
+ Throwable.class.getMethod("getCause", (Class[])null);
+ supportsNestedThrowable = Boolean.TRUE;
+ getLog().debug("Detected JDK support for nested exceptions.");
+ } catch (NoSuchMethodException e) {
+ supportsNestedThrowable = Boolean.FALSE;
+ getLog().debug("Nested exceptions are not supported by this JDK.");
+ }
+ }
+
+ return supportsNestedThrowable.booleanValue();
+ }
+
+ private static Log getLog() {
+ return LogFactory.getLog(ExceptionHelper.class);
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/JNDIConnectionProvider.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/JNDIConnectionProvider.java
new file mode 100644
index 000000000..35c3f9f2f
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/JNDIConnectionProvider.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.utils;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.sql.DataSource;
+import javax.sql.XADataSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ *
+ * A ConnectionProvider
that provides connections from a DataSource
+ * that is managed by an application server, and made available via JNDI.
+ *
+ *
+ * @see DBConnectionManager
+ * @see ConnectionProvider
+ * @see PoolingConnectionProvider
+ *
+ * @author James House
+ * @author Sharada Jambula
+ * @author Mohammad Rezaei
+ * @author Patrick Lightbody
+ * @author Srinivas Venkatarangaiah
+ */
+public class JNDIConnectionProvider implements ConnectionProvider {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private String url;
+
+ private Properties props;
+
+ private Object datasource;
+
+ private boolean alwaysLookup = false;
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Constructor
+ *
+ * @param jndiUrl
+ * The url for the datasource
+ */
+ public JNDIConnectionProvider(String jndiUrl, boolean alwaysLookup) {
+ this.url = jndiUrl;
+ this.alwaysLookup = alwaysLookup;
+ init();
+ }
+
+ /**
+ * Constructor
+ *
+ * @param jndiUrl
+ * The URL for the DataSource
+ * @param jndiProps
+ * The JNDI properties to use when establishing the InitialContext
+ * for the lookup of the given URL.
+ */
+ public JNDIConnectionProvider(String jndiUrl, Properties jndiProps,
+ boolean alwaysLookup) {
+ this.url = jndiUrl;
+ this.props = jndiProps;
+ this.alwaysLookup = alwaysLookup;
+ init();
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ protected Log getLog() {
+ return log;
+ }
+
+ private void init() {
+
+ if (!isAlwaysLookup()) {
+ Context ctx = null;
+ try {
+ ctx = (props != null) ? new InitialContext(props) : new InitialContext();
+
+ datasource = (DataSource) ctx.lookup(url);
+ } catch (Exception e) {
+ getLog().error(
+ "Error looking up datasource: " + e.getMessage(), e);
+ } finally {
+ if (ctx != null) {
+ try { ctx.close(); } catch(Exception ignore) {}
+ }
+ }
+ }
+ }
+
+ public Connection getConnection() throws SQLException {
+ Context ctx = null;
+ try {
+ Object ds = this.datasource;
+
+ if (ds == null || isAlwaysLookup()) {
+ ctx = (props != null) ? new InitialContext(props): new InitialContext();
+
+ ds = ctx.lookup(url);
+ if (!isAlwaysLookup()) {
+ this.datasource = ds;
+ }
+ }
+
+ if (ds == null) {
+ throw new SQLException( "There is no object at the JNDI URL '" + url + "'");
+ }
+
+ if (ds instanceof XADataSource) {
+ return (((XADataSource) ds).getXAConnection().getConnection());
+ } else if (ds instanceof DataSource) {
+ return ((DataSource) ds).getConnection();
+ } else {
+ throw new SQLException("Object at JNDI URL '" + url + "' is not a DataSource.");
+ }
+ } catch (Exception e) {
+ this.datasource = null;
+ throw new SQLException(
+ "Could not retrieve datasource via JNDI url '" + url + "' "
+ + e.getClass().getName() + ": " + e.getMessage());
+ } finally {
+ if (ctx != null) {
+ try { ctx.close(); } catch(Exception ignore) {}
+ }
+ }
+ }
+
+ public boolean isAlwaysLookup() {
+ return alwaysLookup;
+ }
+
+ public void setAlwaysLookup(boolean b) {
+ alwaysLookup = b;
+ }
+
+ /*
+ * @see com.fr.third.org.quartz.utils.ConnectionProvider#shutdown()
+ */
+ public void shutdown() throws SQLException {
+ // do nothing
+ }
+
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/Key.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/Key.java
new file mode 100644
index 000000000..b8f8dfb21
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/Key.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.utils;
+
+/**
+ *
+ * Object representing a job or trigger key.
+ *
+ *
+ * @author Jeffrey Wescott
+ */
+public class Key extends Pair {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Construct a new key with the given name and group.
+ *
+ * @param name
+ * the name
+ * @param group
+ * the group
+ */
+ public Key(String name, String group) {
+ super();
+ super.setFirst(name);
+ super.setSecond(group);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Get the name portion of the key.
+ *
+ *
+ * @return the name
+ */
+ public String getName() {
+ return (String) getFirst();
+ }
+
+ /**
+ *
+ * Get the group portion of the key.
+ *
+ *
+ * @return the group
+ */
+ public String getGroup() {
+ return (String) getSecond();
+ }
+
+ /**
+ *
+ * Return the string representation of the key. The format will be:
+ * <group>.<name>.
+ *
+ *
+ * @return the string representation of the key
+ */
+ public String toString() {
+ return getGroup() + '.' + getName();
+ }
+}
+
+// EOF
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/Pair.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/Pair.java
new file mode 100644
index 000000000..f997764eb
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/Pair.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+
+package com.fr.third.org.quartz.utils;
+
+/**
+ *
+ * Utility class for storing two pieces of information together.
+ *
+ *
+ * @author Jeffrey Wescott
+ */
+public class Pair {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private Object first;
+
+ private Object second;
+
+
+ public Pair()
+ {}
+
+ public Pair(Object first, Object second)
+ {
+ setFirst(first);
+ setSecond(second);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ *
+ * Get the first object in the pair.
+ *
+ *
+ * @return the first object
+ */
+ public final Object getFirst() {
+ return first;
+ }
+
+ /**
+ *
+ * Set the value of the first object in the pair.
+ *
+ *
+ * @param first
+ * the first object
+ */
+ public final void setFirst(Object first) {
+ this.first = first;
+ }
+
+ /**
+ *
+ * Get the second object in the pair.
+ *
+ *
+ * @return the second object
+ */
+ public final Object getSecond() {
+ return second;
+ }
+
+ /**
+ *
+ * Set the second object in the pair.
+ *
+ *
+ * @param second
+ * the second object
+ */
+ public final void setSecond(Object second) {
+ this.second = second;
+ }
+
+ /**
+ *
+ * Test equality of this object with that.
+ *
+ *
+ * @param that
+ * object to compare
+ * @return true if objects are equal, false otherwise
+ */
+ public boolean equals(Object that) {
+ if (this == that) {
+ return true;
+ } else {
+ try {
+ Pair other = (Pair) that;
+ if(first == null && second == null)
+ return (other.first == null && other.second == null);
+ else if(first == null)
+ return this.second.equals(other.second);
+ else if(second == null)
+ return this.first.equals(other.first);
+ else
+ return (this.first.equals(other.first) && this.second
+ .equals(other.second));
+
+ } catch (ClassCastException e) {
+ return false;
+ }
+ }
+ }
+
+ public int hashCode() {
+ if(first != null && second != null)
+ return (first.hashCode() ^ second.hashCode());
+ if(first != null)
+ return (17 ^ first.hashCode());
+ if(second != null)
+ return (17 ^ second.hashCode());
+ return super.hashCode();
+ }
+}
+
+// EOF
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/PoolingConnectionProvider.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/PoolingConnectionProvider.java
new file mode 100644
index 000000000..d2f8892bf
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/PoolingConnectionProvider.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.utils;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import com.fr.third.alibaba.druid.pool.DruidDataSource;
+
+/**
+ *
+ * A ConnectionProvider
implementation that creates its own
+ * pool of connections.
+ *
+ *
+ *
+ * This class uses DBCP,
+ * an Apache-Jakarta-Commons product.
+ *
+ *
+ * @see DBConnectionManager
+ * @see ConnectionProvider
+ *
+ * @author Sharada Jambula
+ * @author James House
+ * @author Mohammad Rezaei
+ */
+public class PoolingConnectionProvider implements ConnectionProvider {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constants.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /** The JDBC database driver. */
+ public static final String DB_DRIVER = "driver";
+
+ /** The JDBC database URL. */
+ public static final String DB_URL = "URL";
+
+ /** The database user name. */
+ public static final String DB_USER = "user";
+
+ /** The database user password. */
+ public static final String DB_PASSWORD = "password";
+
+ /** The maximum number of database connections to have in the pool. */
+ public static final String DB_MAX_CONNECTIONS = "maxConnections";
+
+ /**
+ * The database sql query to execute everytime a connection is retrieved
+ * from the pool to ensure that it is still valid.
+ */
+ public static final String DB_VALIDATION_QUERY = "validationQuery";
+
+ /** Default maximum number of database connections in the pool. */
+ public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private DruidDataSource datasource;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public PoolingConnectionProvider(String dbDriver, String dbURL,
+ String dbUser, String dbPassword, int maxConnections,
+ String dbValidationQuery) throws SQLException {
+ initialize(
+ dbDriver, dbURL, dbUser, dbPassword,
+ maxConnections, dbValidationQuery);
+ }
+
+ /**
+ * Create a connection pool using the given properties.
+ *
+ *
+ * The properties passed should contain:
+ *
+ * - {@link #DB_DRIVER}- The database driver class name
+ *
- {@link #DB_URL}- The database URL
+ *
- {@link #DB_USER}- The database user
+ *
- {@link #DB_PASSWORD}- The database password
+ *
- {@link #DB_MAX_CONNECTIONS}- The maximum # connections in the pool,
+ * optional
+ *
- {@link #DB_VALIDATION_QUERY}- The sql validation query, optional
+ *
+ *
+ *
+ * @param config
+ * configuration properties
+ */
+ public PoolingConnectionProvider(Properties config) throws SQLException {
+ PropertiesParser cfg = new PropertiesParser(config);
+ initialize(
+ cfg.getStringProperty(DB_DRIVER),
+ cfg.getStringProperty(DB_URL),
+ cfg.getStringProperty(DB_USER, ""),
+ cfg.getStringProperty(DB_PASSWORD, ""),
+ cfg.getIntProperty(DB_MAX_CONNECTIONS, DEFAULT_DB_MAX_CONNECTIONS),
+ cfg.getStringProperty(DB_VALIDATION_QUERY));
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Create the underlying DBCP BasicDataSource with the
+ * default supported properties.
+ */
+ private void initialize(
+ String dbDriver,
+ String dbURL,
+ String dbUser,
+ String dbPassword,
+ int maxConnections,
+ String dbValidationQuery) throws SQLException {
+ if (dbURL == null) {
+ throw new SQLException(
+ "DBPool could not be created: DB URL cannot be null");
+ }
+
+ if (dbDriver == null) {
+ throw new SQLException(
+ "DBPool '" + dbURL + "' could not be created: " +
+ "DB driver class name cannot be null!");
+ }
+
+ if (maxConnections < 0) {
+ throw new SQLException(
+ "DBPool '" + dbURL + "' could not be created: " +
+ "Max connections must be greater than zero!");
+ }
+
+ datasource = new DruidDataSource();
+ datasource.setDriverClassName(dbDriver);
+ datasource.setUrl(dbURL);
+ datasource.setUsername(dbUser);
+ datasource.setPassword(dbPassword);
+ datasource.setMaxActive(maxConnections);
+ if (dbValidationQuery != null) {
+ datasource.setValidationQuery(dbValidationQuery);
+ }
+ }
+
+ /**
+ * Get the DBCP BasicDataSource created during initialization.
+ *
+ *
+ * This can be used to set additional data source properties in a
+ * subclass's constructor.
+ *
+ */
+ protected DruidDataSource getDataSource() {
+ return datasource;
+ }
+
+ public Connection getConnection() throws SQLException {
+ return datasource.getConnection();
+ }
+
+ public void shutdown() throws SQLException {
+ datasource.close();
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/PropertiesParser.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/PropertiesParser.java
new file mode 100644
index 000000000..69e879f43
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/PropertiesParser.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package com.fr.third.org.quartz.utils;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ *
+ * This is an utility calss used to parse the properties.
+ *
+ *
+ * @author James House
+ */
+public class PropertiesParser {
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ Properties props = null;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public PropertiesParser(Properties props) {
+ this.props = props;
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public Properties getUnderlyingProperties() {
+ return props;
+ }
+
+ /**
+ * Get the trimmed String value of the property with the given
+ * name
. If the value the empty String (after
+ * trimming), then it returns null.
+ */
+ public String getStringProperty(String name) {
+ return getStringProperty(name, null);
+ }
+
+ /**
+ * Get the trimmed String value of the property with the given
+ * name
or the given default value if the value is
+ * null or empty after trimming.
+ */
+ public String getStringProperty(String name, String def) {
+ String val = props.getProperty(name, def);
+ if (val == null) {
+ return def;
+ }
+
+ val = val.trim();
+
+ return (val.length() == 0) ? def : val;
+ }
+
+ public String[] getStringArrayProperty(String name) {
+ return getStringArrayProperty(name, null);
+ }
+
+ public String[] getStringArrayProperty(String name, String[] def) {
+ String vals = getStringProperty(name);
+ if (vals == null) {
+ return def;
+ }
+
+ StringTokenizer stok = new StringTokenizer(vals, ",");
+ ArrayList strs = new ArrayList();
+ try {
+ while (stok.hasMoreTokens()) {
+ strs.add(stok.nextToken().trim());
+ }
+
+ return (String[])strs.toArray(new String[strs.size()]);
+ } catch (Exception e) {
+ return def;
+ }
+ }
+
+ public boolean getBooleanProperty(String name) {
+ return getBooleanProperty(name, false);
+ }
+
+ public boolean getBooleanProperty(String name, boolean def) {
+ String val = getStringProperty(name);
+
+ return (val == null) ? def : Boolean.valueOf(val).booleanValue();
+ }
+
+ public byte getByteProperty(String name) throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ throw new NumberFormatException(" null string");
+ }
+
+ try {
+ return Byte.parseByte(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public byte getByteProperty(String name, byte def)
+ throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ return def;
+ }
+
+ try {
+ return Byte.parseByte(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public char getCharProperty(String name) {
+ return getCharProperty(name, '\0');
+ }
+
+ public char getCharProperty(String name, char def) {
+ String param = getStringProperty(name);
+ return (param == null) ? def : param.charAt(0);
+ }
+
+ public double getDoubleProperty(String name) throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ throw new NumberFormatException(" null string");
+ }
+
+ try {
+ return Double.parseDouble(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public double getDoubleProperty(String name, double def)
+ throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ return def;
+ }
+
+ try {
+ return Double.parseDouble(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public float getFloatProperty(String name) throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ throw new NumberFormatException(" null string");
+ }
+
+ try {
+ return Float.parseFloat(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public float getFloatProperty(String name, float def)
+ throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ return def;
+ }
+
+ try {
+ return Float.parseFloat(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public int getIntProperty(String name) throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ throw new NumberFormatException(" null string");
+ }
+
+ try {
+ return Integer.parseInt(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public int getIntProperty(String name, int def)
+ throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ return def;
+ }
+
+ try {
+ return Integer.parseInt(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public int[] getIntArrayProperty(String name) throws NumberFormatException {
+ return getIntArrayProperty(name, null);
+ }
+
+ public int[] getIntArrayProperty(String name, int[] def)
+ throws NumberFormatException {
+ String vals = getStringProperty(name);
+ if (vals == null) {
+ return def;
+ }
+
+ StringTokenizer stok = new StringTokenizer(vals, ",");
+ ArrayList ints = new ArrayList();
+ try {
+ while (stok.hasMoreTokens()) {
+ try {
+ ints.add(new Integer(stok.nextToken().trim()));
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + vals + "'");
+ }
+ }
+
+ int[] outInts = new int[ints.size()];
+ for (int i = 0; i < ints.size(); i++) {
+ outInts[i] = ((Integer)ints.get(i)).intValue();
+ }
+ return outInts;
+ } catch (Exception e) {
+ return def;
+ }
+ }
+
+ public long getLongProperty(String name) throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ throw new NumberFormatException(" null string");
+ }
+
+ try {
+ return Long.parseLong(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public long getLongProperty(String name, long def)
+ throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ return def;
+ }
+
+ try {
+ return Long.parseLong(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public short getShortProperty(String name) throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ throw new NumberFormatException(" null string");
+ }
+
+ try {
+ return Short.parseShort(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public short getShortProperty(String name, short def)
+ throws NumberFormatException {
+ String val = getStringProperty(name);
+ if (val == null) {
+ return def;
+ }
+
+ try {
+ return Short.parseShort(val);
+ } catch (NumberFormatException nfe) {
+ throw new NumberFormatException(" '" + val + "'");
+ }
+ }
+
+ public String[] getPropertyGroups(String prefix) {
+ Enumeration keys = props.propertyNames();
+ HashSet groups = new HashSet(10);
+
+ if (!prefix.endsWith(".")) {
+ prefix += ".";
+ }
+
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (key.startsWith(prefix)) {
+ String groupName = key.substring(prefix.length(), key.indexOf(
+ '.', prefix.length()));
+ groups.add(groupName);
+ }
+ }
+
+ return (String[]) groups.toArray(new String[groups.size()]);
+ }
+
+ public Properties getPropertyGroup(String prefix) {
+ return getPropertyGroup(prefix, false, null);
+ }
+
+ public Properties getPropertyGroup(String prefix, boolean stripPrefix) {
+ return getPropertyGroup(prefix, stripPrefix, null);
+ }
+
+ /**
+ * Get all properties that start with the given prefix.
+ *
+ * @param prefix The prefix for which to search. If it does not end in
+ * a "." then one will be added to it for search purposes.
+ * @param stripPrefix Whether to strip off the given prefix
+ * in the result's keys.
+ * @param excludedPrefixes Optional array of fully qualified prefixes to
+ * exclude. For example if prefix
is "a.b.c", then
+ * excludedPrefixes
might be "a.b.c.ignore".
+ *
+ * @return Group of Properties
that start with the given prefix,
+ * optionally have that prefix removed, and do not include properties
+ * that start with one of the given excluded prefixes.
+ */
+ public Properties getPropertyGroup(String prefix, boolean stripPrefix, String[] excludedPrefixes) {
+ Enumeration keys = props.propertyNames();
+ Properties group = new Properties();
+
+ if (!prefix.endsWith(".")) {
+ prefix += ".";
+ }
+
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ if (key.startsWith(prefix)) {
+
+ boolean exclude = false;
+ if (excludedPrefixes != null) {
+ for (int i = 0; (i < excludedPrefixes.length) && (exclude == false); i++) {
+ exclude = key.startsWith(excludedPrefixes[i]);
+ }
+ }
+
+ if (exclude == false) {
+ String value = getStringProperty(key, "");
+
+ if (stripPrefix) {
+ group.put(key.substring(prefix.length()), value);
+ } else {
+ group.put(key, value);
+ }
+ }
+ }
+ }
+
+ return group;
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/StringKeyDirtyFlagMap.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/StringKeyDirtyFlagMap.java
new file mode 100644
index 000000000..46dd4b819
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/StringKeyDirtyFlagMap.java
@@ -0,0 +1,362 @@
+/*
+ * Copyright 2004-2006 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.fr.third.org.quartz.utils;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ *
+ * An implementation of Map
that wraps another Map
+ * and flags itself 'dirty' when it is modified, enforces that all keys are
+ * Strings.
+ *
+ *
+ *
+ * All allowsTransientData flag related methods are deprecated as of version 1.6.
+ *
+ */
+public class StringKeyDirtyFlagMap extends DirtyFlagMap {
+ static final long serialVersionUID = -9076749120524952280L;
+
+ /**
+ * @deprecated JDBCJobStores no longer prune out transient data. If you
+ * include non-Serializable values in the Map, you will now get an
+ * exception when attempting to store it in a database.
+ */
+ private boolean allowsTransientData = false;
+
+ public StringKeyDirtyFlagMap() {
+ super();
+ }
+
+ public StringKeyDirtyFlagMap(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ public StringKeyDirtyFlagMap(int initialCapacity, float loadFactor) {
+ super(initialCapacity, loadFactor);
+ }
+
+ /**
+ * Get a copy of the Map's String keys in an array of Strings.
+ */
+ public String[] getKeys() {
+ return (String[]) keySet().toArray(new String[size()]);
+ }
+
+ /**
+ * Tell the StringKeyDirtyFlagMap
that it should
+ * allow non-Serializable
values. Enforces that the Map
+ * doesn't already include transient data.
+ *
+ * @deprecated JDBCJobStores no longer prune out transient data. If you
+ * include non-Serializable values in the Map, you will now get an
+ * exception when attempting to store it in a database.
+ */
+ public void setAllowsTransientData(boolean allowsTransientData) {
+
+ if (containsTransientData() && !allowsTransientData) {
+ throw new IllegalStateException(
+ "Cannot set property 'allowsTransientData' to 'false' "
+ + "when data map contains non-serializable objects.");
+ }
+
+ this.allowsTransientData = allowsTransientData;
+ }
+
+ /**
+ * Whether the StringKeyDirtyFlagMap
allows
+ * non-Serializable
values.
+ *
+ * @deprecated JDBCJobStores no longer prune out transient data. If you
+ * include non-Serializable values in the Map, you will now get an
+ * exception when attempting to store it in a database.
+ */
+ public boolean getAllowsTransientData() {
+ return allowsTransientData;
+ }
+
+ /**
+ * Determine whether any values in this Map do not implement
+ * Serializable
. Always returns false if this Map
+ * is flagged to not allow transient data.
+ *
+ * @deprecated JDBCJobStores no longer prune out transient data. If you
+ * include non-Serializable values in the Map, you will now get an
+ * exception when attempting to store it in a database.
+ */
+ public boolean containsTransientData() {
+ if (!getAllowsTransientData()) { // short circuit...
+ return false;
+ }
+
+ String[] keys = getKeys();
+ for (int i = 0; i < keys.length; i++) {
+ Object o = super.get(keys[i]);
+ if (!(o instanceof Serializable)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Removes any data values in the map that are non-Serializable. Does
+ * nothing if this Map does not allow transient data.
+ *
+ * @deprecated JDBCJobStores no longer prune out transient data. If you
+ * include non-Serializable values in the Map, you will now get an
+ * exception when attempting to store it in a database.
+ */
+ public void removeTransientData() {
+ if (!getAllowsTransientData()) { // short circuit...
+ return;
+ }
+
+ String[] keys = getKeys();
+ for (int i = 0; i < keys.length; i++) {
+ Object o = super.get(keys[i]);
+ if (!(o instanceof Serializable)) {
+ remove(keys[i]);
+ }
+ }
+ }
+
+ /**
+ *
+ * Adds the name-value pairs in the given Map
to the
+ * StringKeyDirtyFlagMap
.
+ *
+ *
+ *
+ * All keys must be String
s.
+ *
+ */
+ public void putAll(Map map) {
+ for (Iterator entryIter = map.entrySet().iterator(); entryIter.hasNext();) {
+ Map.Entry entry = (Map.Entry) entryIter.next();
+
+ // will throw IllegalArgumentException if key is not a String
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ *
+ * Adds the given int
value to the StringKeyDirtyFlagMap
.
+ *
+ */
+ public void put(String key, int value) {
+ super.put(key, new Integer(value));
+ }
+
+ /**
+ *
+ * Adds the given long
value to the StringKeyDirtyFlagMap
.
+ *
+ */
+ public void put(String key, long value) {
+ super.put(key, new Long(value));
+ }
+
+ /**
+ *
+ * Adds the given float
value to the StringKeyDirtyFlagMap
.
+ *
+ */
+ public void put(String key, float value) {
+ super.put(key, new Float(value));
+ }
+
+ /**
+ *
+ * Adds the given double
value to the StringKeyDirtyFlagMap
.
+ *
+ */
+ public void put(String key, double value) {
+ super.put(key, new Double(value));
+ }
+
+ /**
+ *
+ * Adds the given boolean
value to the StringKeyDirtyFlagMap
.
+ *
+ */
+ public void put(String key, boolean value) {
+ super.put(key, new Boolean(value));
+ }
+
+ /**
+ *
+ * Adds the given char
value to the StringKeyDirtyFlagMap
.
+ *
+ */
+ public void put(String key, char value) {
+ super.put(key, new Character(value));
+ }
+
+ /**
+ *
+ * Adds the given String
value to the StringKeyDirtyFlagMap
.
+ *
+ */
+ public void put(String key, String value) {
+ super.put(key, value);
+ }
+
+ /**
+ *
+ * Adds the given Object
value to the StringKeyDirtyFlagMap
.
+ *
+ */
+ public Object put(Object key, Object value) {
+ if (!(key instanceof String)) {
+ throw new IllegalArgumentException(
+ "Keys in map must be Strings.");
+ }
+
+ return super.put(key, value);
+ }
+
+ /**
+ *
+ * Retrieve the identified int
value from the StringKeyDirtyFlagMap
.
+ *
+ *
+ * @throws ClassCastException
+ * if the identified object is not an Integer.
+ */
+ public int getInt(String key) {
+ Object obj = get(key);
+
+ try {
+ return ((Integer) obj).intValue();
+ } catch (Exception e) {
+ throw new ClassCastException("Identified object is not an Integer.");
+ }
+ }
+
+ /**
+ *
+ * Retrieve the identified long
value from the StringKeyDirtyFlagMap
.
+ *
+ *
+ * @throws ClassCastException
+ * if the identified object is not a Long.
+ */
+ public long getLong(String key) {
+ Object obj = get(key);
+
+ try {
+ return ((Long) obj).longValue();
+ } catch (Exception e) {
+ throw new ClassCastException("Identified object is not a Long.");
+ }
+ }
+
+ /**
+ *
+ * Retrieve the identified float
value from the StringKeyDirtyFlagMap
.
+ *
+ *
+ * @throws ClassCastException
+ * if the identified object is not a Float.
+ */
+ public float getFloat(String key) {
+ Object obj = get(key);
+
+ try {
+ return ((Float) obj).floatValue();
+ } catch (Exception e) {
+ throw new ClassCastException("Identified object is not a Float.");
+ }
+ }
+
+ /**
+ *
+ * Retrieve the identified double
value from the StringKeyDirtyFlagMap
.
+ *
+ *
+ * @throws ClassCastException
+ * if the identified object is not a Double.
+ */
+ public double getDouble(String key) {
+ Object obj = get(key);
+
+ try {
+ return ((Double) obj).doubleValue();
+ } catch (Exception e) {
+ throw new ClassCastException("Identified object is not a Double.");
+ }
+ }
+
+ /**
+ *
+ * Retrieve the identified boolean
value from the StringKeyDirtyFlagMap
.
+ *
+ *
+ * @throws ClassCastException
+ * if the identified object is not a Boolean.
+ */
+ public boolean getBoolean(String key) {
+ Object obj = get(key);
+
+ try {
+ return ((Boolean) obj).booleanValue();
+ } catch (Exception e) {
+ throw new ClassCastException("Identified object is not a Boolean.");
+ }
+ }
+
+ /**
+ *
+ * Retrieve the identified char
value from the StringKeyDirtyFlagMap
.
+ *
+ *
+ * @throws ClassCastException
+ * if the identified object is not a Character.
+ */
+ public char getChar(String key) {
+ Object obj = get(key);
+
+ try {
+ return ((Character) obj).charValue();
+ } catch (Exception e) {
+ throw new ClassCastException("Identified object is not a Character.");
+ }
+ }
+
+ /**
+ *
+ * Retrieve the identified String
value from the StringKeyDirtyFlagMap
.
+ *
+ *
+ * @throws ClassCastException
+ * if the identified object is not a String.
+ */
+ public String getString(String key) {
+ Object obj = get(key);
+
+ try {
+ return (String) obj;
+ } catch (Exception e) {
+ throw new ClassCastException("Identified object is not a String.");
+ }
+ }
+}
diff --git a/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/TriggerStatus.java b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/TriggerStatus.java
new file mode 100644
index 000000000..f3a9b6b29
--- /dev/null
+++ b/fine-quartz-old/src/main/java/com/fr/third/org/quartz/utils/TriggerStatus.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2004-2005 OpenSymphony
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+/*
+ * Previously Copyright (c) 2001-2004 James House
+ */
+
+package com.fr.third.org.quartz.utils;
+
+import java.util.Date;
+
+/**
+ *
+ * Object representing a job or trigger key.
+ *
+ *
+ * @author James House
+ */
+public class TriggerStatus extends Pair {
+
+ // TODO: Repackage under spi or root pkg ?, put status constants here.
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Data members.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ private Key key;
+
+ private Key jobKey;
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Constructors.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ /**
+ * Construct a new TriggerStatus with the status name and nextFireTime.
+ *
+ * @param status
+ * the trigger's status
+ * @param nextFireTime
+ * the next time the trigger will fire
+ */
+ public TriggerStatus(String status, Date nextFireTime) {
+ super();
+ super.setFirst(status);
+ super.setSecond(nextFireTime);
+ }
+
+ /*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Interface.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+ public Key getJobKey() {
+ return jobKey;
+ }
+
+ public void setJobKey(Key jobKey) {
+ this.jobKey = jobKey;
+ }
+
+ public Key getKey() {
+ return key;
+ }
+
+ public void setKey(Key key) {
+ this.key = key;
+ }
+
+ /**
+ *
+ * Get the name portion of the key.
+ *
+ *
+ * @return the name
+ */
+ public String getStatus() {
+ return (String) getFirst();
+ }
+
+ /**
+ *
+ * Get the group portion of the key.
+ *
+ *
+ * @return the group
+ */
+ public Date getNextFireTime() {
+ return (Date) getSecond();
+ }
+
+ /**
+ *
+ * Return the string representation of the TriggerStatus.
+ *
+ *
+ */
+ public String toString() {
+ return "status: " + getStatus() + ", next Fire = " + getNextFireTime();
+ }
+}
+
+// EOF