Index: 3rdParty_sources/quartz/org/quartz/Calendar.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/Calendar.java (.../Calendar.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/Calendar.java (.../Calendar.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,24 +16,29 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ - - package org.quartz; /** - *

- * An interface to be implemented by objects that define spaces of time that - * should be included or excluded from a {@link Trigger}'s - * normal 'firing' schedule. - *

+ * An interface to be implemented by objects that define spaces of time during + * which an associated {@link Trigger} may (not) 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)

+ * + *

Implementations MUST take care of being properly Cloneable + * and Serializable.

+ * * @author James House * @author Juergen Donnerstag */ -public interface Calendar extends java.io.Serializable { +public interface Calendar extends java.io.Serializable, java.lang.Cloneable { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -58,30 +63,30 @@ * Set a new base calendar or remove the existing one. *

*/ - public void setBaseCalendar(Calendar baseCalendar); + void setBaseCalendar(Calendar baseCalendar); /** *

* Get the base calendar. Will be null, if not set. *

*/ - public Calendar getBaseCalendar(); + Calendar getBaseCalendar(); /** *

* Determine whether the given time (in milliseconds) is 'included' by the * Calendar. *

*/ - public boolean isTimeIncluded(long timeStamp); + boolean isTimeIncluded(long timeStamp); /** *

* Determine the next time (in milliseconds) that is 'included' by the * Calendar after the given time. *

*/ - public long getNextIncludedTime(long timeStamp); + long getNextIncludedTime(long timeStamp); /** *

@@ -91,7 +96,7 @@ * * @return null if no description was set. */ - public String getDescription(); + String getDescription(); /** *

@@ -100,5 +105,7 @@ * the description has no meaning to Quartz. *

*/ - public void setDescription(String description); + void setDescription(String description); + + Object clone(); } Index: 3rdParty_sources/quartz/org/quartz/CalendarIntervalScheduleBuilder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/CalendarIntervalScheduleBuilder.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/CalendarIntervalScheduleBuilder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,339 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.util.TimeZone; + +import org.quartz.DateBuilder.IntervalUnit; +import org.quartz.impl.triggers.CalendarIntervalTriggerImpl; +import org.quartz.spi.MutableTrigger; + +/** + * CalendarIntervalScheduleBuilder is a {@link ScheduleBuilder} + * that defines calendar time (day, week, month, year) interval-based + * schedules for Triggers. + * + *

Quartz provides a builder-style API for constructing scheduling-related + * entities via a Domain-Specific Language (DSL). The DSL can best be + * utilized through the usage of static imports of the methods on the classes + * TriggerBuilder, JobBuilder, + * DateBuilder, JobKey, TriggerKey + * and the various ScheduleBuilder implementations.

+ * + *

Client code can then use the DSL to write code such as this:

+ *
+ *         JobDetail job = newJob(MyJob.class)
+ *             .withIdentity("myJob")
+ *             .build();
+ *             
+ *         Trigger trigger = newTrigger() 
+ *             .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+ *             .withSchedule(withIntervalInDays(3))
+ *             .startAt(futureDate(10, MINUTES))
+ *             .build();
+ *         
+ *         scheduler.scheduleJob(job, trigger);
+ * 
+ *
+ * @see DailyTimeIntervalScheduleBuilder
+ * @see CronScheduleBuilder
+ * @see ScheduleBuilder
+ * @see SimpleScheduleBuilder 
+ * @see TriggerBuilder
+ */
+public class CalendarIntervalScheduleBuilder extends ScheduleBuilder {
+
+    private int interval = 1;
+    private IntervalUnit intervalUnit = IntervalUnit.DAY;
+
+    private int misfireInstruction = CalendarIntervalTrigger.MISFIRE_INSTRUCTION_SMART_POLICY;
+    private TimeZone timeZone;
+    private boolean preserveHourOfDayAcrossDaylightSavings;
+    private boolean skipDayIfHourDoesNotExist;
+    
+    protected CalendarIntervalScheduleBuilder() {
+    }
+    
+    /**
+     * Create a CalendarIntervalScheduleBuilder.
+     * 
+     * @return the new CalendarIntervalScheduleBuilder
+     */
+    public static CalendarIntervalScheduleBuilder calendarIntervalSchedule() {
+        return new CalendarIntervalScheduleBuilder();
+    }
+    
+    /**
+     * Build the actual Trigger -- NOT intended to be invoked by end users,
+     * but will rather be invoked by a TriggerBuilder which this 
+     * ScheduleBuilder is given to.
+     * 
+     * @see TriggerBuilder#withSchedule(ScheduleBuilder)
+     */
+    @Override
+    public MutableTrigger build() {
+
+        CalendarIntervalTriggerImpl st = new CalendarIntervalTriggerImpl();
+        st.setRepeatInterval(interval);
+        st.setRepeatIntervalUnit(intervalUnit);
+        st.setMisfireInstruction(misfireInstruction);
+        st.setTimeZone(timeZone);
+        st.setPreserveHourOfDayAcrossDaylightSavings(preserveHourOfDayAcrossDaylightSavings);
+        st.setSkipDayIfHourDoesNotExist(skipDayIfHourDoesNotExist);
+
+        return st;
+    }
+
+    /**
+     * Specify the time unit and interval for the Trigger to be produced.
+     * 
+     * @param timeInterval the interval at which the trigger should repeat.
+     * @param unit  the time unit (IntervalUnit) of the interval.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#getRepeatInterval()
+     * @see CalendarIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public CalendarIntervalScheduleBuilder withInterval(int timeInterval, IntervalUnit unit) {
+        if(unit == null)
+            throw new IllegalArgumentException("TimeUnit must be specified.");
+        validateInterval(timeInterval);
+        this.interval = timeInterval;
+        this.intervalUnit = unit;
+        return this;
+    }
+
+    /**
+     * Specify an interval in the IntervalUnit.SECOND that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInSeconds the number of seconds at which the trigger should repeat.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#getRepeatInterval()
+     * @see CalendarIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public CalendarIntervalScheduleBuilder withIntervalInSeconds(int intervalInSeconds) {
+        validateInterval(intervalInSeconds);
+        this.interval = intervalInSeconds;
+        this.intervalUnit = IntervalUnit.SECOND;
+        return this;
+    }
+    
+    /**
+     * Specify an interval in the IntervalUnit.MINUTE that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInMinutes the number of minutes at which the trigger should repeat.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#getRepeatInterval()
+     * @see CalendarIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public CalendarIntervalScheduleBuilder withIntervalInMinutes(int intervalInMinutes) {
+        validateInterval(intervalInMinutes);
+        this.interval = intervalInMinutes;
+        this.intervalUnit = IntervalUnit.MINUTE;
+        return this;
+    }
+
+    /**
+     * Specify an interval in the IntervalUnit.HOUR that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInHours the number of hours at which the trigger should repeat.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#getRepeatInterval()
+     * @see CalendarIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public CalendarIntervalScheduleBuilder withIntervalInHours(int intervalInHours) {
+        validateInterval(intervalInHours);
+        this.interval = intervalInHours;
+        this.intervalUnit = IntervalUnit.HOUR;
+        return this;
+    }
+    
+    /**
+     * Specify an interval in the IntervalUnit.DAY that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInDays the number of days at which the trigger should repeat.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#getRepeatInterval()
+     * @see CalendarIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public CalendarIntervalScheduleBuilder withIntervalInDays(int intervalInDays) {
+        validateInterval(intervalInDays);
+        this.interval = intervalInDays;
+        this.intervalUnit = IntervalUnit.DAY;
+        return this;
+    }
+
+    /**
+     * Specify an interval in the IntervalUnit.WEEK that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInWeeks the number of weeks at which the trigger should repeat.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#getRepeatInterval()
+     * @see CalendarIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public CalendarIntervalScheduleBuilder withIntervalInWeeks(int intervalInWeeks) {
+        validateInterval(intervalInWeeks);
+        this.interval = intervalInWeeks;
+        this.intervalUnit = IntervalUnit.WEEK;
+        return this;
+    }
+
+    /**
+     * Specify an interval in the IntervalUnit.MONTH that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInMonths the number of months at which the trigger should repeat.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#getRepeatInterval()
+     * @see CalendarIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public CalendarIntervalScheduleBuilder withIntervalInMonths(int intervalInMonths) {
+        validateInterval(intervalInMonths);
+        this.interval = intervalInMonths;
+        this.intervalUnit = IntervalUnit.MONTH;
+        return this;
+    }
+
+    /**
+     * Specify an interval in the IntervalUnit.YEAR that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInYears the number of years at which the trigger should repeat.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#getRepeatInterval()
+     * @see CalendarIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public CalendarIntervalScheduleBuilder withIntervalInYears(int intervalInYears) {
+        validateInterval(intervalInYears);
+        this.interval = intervalInYears;
+        this.intervalUnit = IntervalUnit.YEAR;
+        return this;
+    }
+
+    /**
+     * If the Trigger misfires, use the 
+     * {@link Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY} instruction.
+     * 
+     * @return the updated CronScheduleBuilder
+     * @see Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
+     */
+    public CalendarIntervalScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
+        misfireInstruction = Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY;
+        return this;
+    }
+    
+    /**
+     * If the Trigger misfires, use the 
+     * {@link CalendarIntervalTrigger#MISFIRE_INSTRUCTION_DO_NOTHING} instruction.
+     * 
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
+     */
+    public CalendarIntervalScheduleBuilder withMisfireHandlingInstructionDoNothing() {
+        misfireInstruction = CalendarIntervalTrigger.MISFIRE_INSTRUCTION_DO_NOTHING;
+        return this;
+    }
+    
+    /**
+     * If the Trigger misfires, use the 
+     * {@link CalendarIntervalTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW} instruction.
+     * 
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
+     */
+    public CalendarIntervalScheduleBuilder withMisfireHandlingInstructionFireAndProceed() {
+        misfireInstruction = CalendarIntervalTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
+        return this;
+    }
+    /**
+     * The TimeZone in which to base the schedule.
+     * 
+     * @param timezone the time-zone for the schedule.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see CalendarIntervalTrigger#getTimeZone()
+     */
+    public CalendarIntervalScheduleBuilder inTimeZone(TimeZone timezone) {
+        this.timeZone = timezone;
+        return this;
+    }
+    
+    /**
+     * If intervals are a day or greater, this property (set to true) will 
+     * cause the firing of the trigger to always occur at the same time of day,
+     * (the time of day of the startTime) regardless of daylight saving time 
+     * transitions.  Default value is false.
+     * 
+     * 

+ * For example, without the property set, your trigger may have a start + * time of 9:00 am on March 1st, and a repeat interval of 2 days. But + * after the daylight saving transition occurs, the trigger may start + * firing at 8:00 am every other day. + *

+ * + *

+ * If however, the time of day does not exist on a given day to fire + * (e.g. 2:00 am in the United States on the days of daylight saving + * transition), the trigger will go ahead and fire one hour off on + * that day, and then resume the normal hour on other days. If + * you wish for the trigger to never fire at the "wrong" hour, then + * you should set the property skipDayIfHourDoesNotExist. + *

+ * + * @see #skipDayIfHourDoesNotExist(boolean) + * @see #inTimeZone(TimeZone) + * @see TriggerBuilder#startAt(java.util.Date) + */ + public CalendarIntervalScheduleBuilder preserveHourOfDayAcrossDaylightSavings(boolean preserveHourOfDay) { + this.preserveHourOfDayAcrossDaylightSavings = preserveHourOfDay; + return this; + } + + /** + * If intervals are a day or greater, and + * preserveHourOfDayAcrossDaylightSavings property is set to true, and the + * hour of the day does not exist on a given day for which the trigger + * would fire, the day will be skipped and the trigger advanced a second + * interval if this property is set to true. Defaults to false. + * + *

+ * CAUTION! If you enable this property, and your hour of day happens + * to be that of daylight savings transition (e.g. 2:00 am in the United + * States) and the trigger's interval would have had the trigger fire on + * that day, then you may actually completely miss a firing on the day of + * transition if that hour of day does not exist on that day! In such a + * case the next fire time of the trigger will be computed as double (if + * the interval is 2 days, then a span of 4 days between firings will + * occur). + *

+ * + * @see #preserveHourOfDayAcrossDaylightSavings(boolean) + */ + public CalendarIntervalScheduleBuilder skipDayIfHourDoesNotExist(boolean skipDay) { + this.skipDayIfHourDoesNotExist = skipDay; + return this; + } + + private void validateInterval(int timeInterval) { + if(timeInterval <= 0) + throw new IllegalArgumentException("Interval must be a positive value."); + } +} Index: 3rdParty_sources/quartz/org/quartz/CalendarIntervalTrigger.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/CalendarIntervalTrigger.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/CalendarIntervalTrigger.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,159 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.util.Calendar; +import java.util.TimeZone; + +import org.quartz.DateBuilder.IntervalUnit; + +/** + * A concrete {@link Trigger} that is used to fire a {@link org.quartz.JobDetail} + * based upon repeating calendar time intervals. + * + *

The trigger will fire every N (see {@link #getRepeatInterval()} ) units of calendar time + * (see {@link #getRepeatIntervalUnit()}) as specified in the trigger's definition. + * This trigger can achieve schedules that are not possible with {@link SimpleTrigger} (e.g + * because months are not a fixed number of seconds) or {@link CronTrigger} (e.g. because + * "every 5 months" is not an even divisor of 12).

+ * + *

If you use an interval unit of MONTH then care should be taken when setting + * a startTime value that is on a day near the end of the month. For example, + * if you choose a start time that occurs on January 31st, and have a trigger with unit + * MONTH and interval 1, then the next fire time will be February 28th, + * and the next time after that will be March 28th - and essentially each subsequent firing will + * occur on the 28th of the month, even if a 31st day exists. If you want a trigger that always + * fires on the last day of the month - regardless of the number of days in the month, + * you should use CronTrigger.

+ * + * @see TriggerBuilder + * @see CalendarIntervalScheduleBuilder + * @see SimpleScheduleBuilder + * @see CronScheduleBuilder + * + * @author James House + */ +public interface CalendarIntervalTrigger extends Trigger { + + /** + *

+ * Instructs the {@link Scheduler} that upon a mis-fire + * situation, the {@link CalendarIntervalTrigger} wants to be + * fired now by Scheduler. + *

+ */ + public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1; + /** + *

+ * Instructs the {@link Scheduler} that upon a mis-fire + * situation, the {@link CalendarIntervalTrigger} 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. + *

+ */ + public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2; + + /** + *

Get the interval unit - the time unit on with the interval applies.

+ */ + public IntervalUnit getRepeatIntervalUnit(); + + /** + *

+ * Get the the time interval that will be added to the DateIntervalTrigger's + * fire time (in the set repeat interval unit) in order to calculate the time of the + * next trigger repeat. + *

+ */ + public int getRepeatInterval(); + + /** + *

+ * Get the number of times the DateIntervalTrigger has already + * fired. + *

+ */ + public int getTimesTriggered(); + + /** + *

+ * Gets the time zone within which time calculations related to this + * trigger will be performed. + *

+ * + *

+ * If null, the system default TimeZone will be used. + *

+ */ + public TimeZone getTimeZone(); + + + /** + * If intervals are a day or greater, this property (set to true) will + * cause the firing of the trigger to always occur at the same time of day, + * (the time of day of the startTime) regardless of daylight saving time + * transitions. Default value is false. + * + *

+ * For example, without the property set, your trigger may have a start + * time of 9:00 am on March 1st, and a repeat interval of 2 days. But + * after the daylight saving transition occurs, the trigger may start + * firing at 8:00 am every other day. + *

+ * + *

+ * If however, the time of day does not exist on a given day to fire + * (e.g. 2:00 am in the United States on the days of daylight saving + * transition), the trigger will go ahead and fire one hour off on + * that day, and then resume the normal hour on other days. If + * you wish for the trigger to never fire at the "wrong" hour, then + * you should set the property skipDayIfHourDoesNotExist. + *

+ * + * @see #isSkipDayIfHourDoesNotExist() + * @see #getStartTime() + * @see #getTimeZone() + */ + public boolean isPreserveHourOfDayAcrossDaylightSavings(); + + /** + * If intervals are a day or greater, and + * preserveHourOfDayAcrossDaylightSavings property is set to true, and the + * hour of the day does not exist on a given day for which the trigger + * would fire, the day will be skipped and the trigger advanced a second + * interval if this property is set to true. Defaults to false. + * + *

+ * CAUTION! If you enable this property, and your hour of day happens + * to be that of daylight savings transition (e.g. 2:00 am in the United + * States) and the trigger's interval would have had the trigger fire on + * that day, then you may actually completely miss a firing on the day of + * transition if that hour of day does not exist on that day! In such a + * case the next fire time of the trigger will be computed as double (if + * the interval is 2 days, then a span of 4 days between firings will + * occur). + *

+ * + * @see #isPreserveHourOfDayAcrossDaylightSavings() + */ + public boolean isSkipDayIfHourDoesNotExist(); + + + TriggerBuilder getTriggerBuilder(); +} Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/CriticalSchedulerException.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/CronExpression.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/CronExpression.java (.../CronExpression.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/CronExpression.java (.../CronExpression.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,3 +1,20 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; import java.io.Serializable; @@ -56,7 +73,7 @@ *   * 1-31 *   - * , - * ? / L W C + * , - * ? / L W * * * Month @@ -75,7 +92,7 @@ * * Year (Optional) *   - * empty, 1970-2099 + * empty, 1970-2199 *   * , - * / * @@ -86,7 +103,7 @@ *

* 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 fileds, but not the other. + * 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". @@ -115,9 +132,10 @@ * 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. + * means "the last friday of the month". You can also specify an offset + * from the last day of the month, such as "L-3" which would mean the third-to-last + * day of the calendar month. When using the 'L' option, it is important not to + * specify lists, or ranges of values, as you'll get confusing/unexpected 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 @@ -142,15 +160,17 @@ * 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. + * 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). *

* + * means "the first day included by the calendar on or after Sunday".--> *

* The legal characters and the names of months and days of the week are not * case sensitive. @@ -159,8 +179,15 @@ * NOTES: *

    *
  • Support for specifying both a day-of-week and a day-of-month value is - * not complete (you'll need to use the '?' character in on of these fields). + * not complete (you'll need to use the '?' character in one of these fields). *
  • + *
  • Overflowing ranges is supported - that is, having a larger number on + * the left hand side than the right. You might do 22-2 to catch 10 o'clock + * at night until 2 o'clock in the morning, or you might have NOV-FEB. It is + * very important to note that overuse of overflowing ranges creates ranges + * that don't make sense and no effort has been made to determine which + * interpretation CronExpression chooses. An example would be + * "0 0 14-6 ? * FRI-MON".
  • *
*

* @@ -169,11 +196,11 @@ * @author Contributions from Mads Henderson * @author Refactoring from CronTrigger to CronExpression by Aaron Craven */ -public class CronExpression implements Serializable, Cloneable { +public final class CronExpression implements Serializable, Cloneable { - private static final long serialVersionUID = 12423409423L; - - protected static final int SECOND = 0; + private static final long serialVersionUID = 12423409423L; + + protected static final int SECOND = 0; protected static final int MINUTE = 1; protected static final int HOUR = 2; protected static final int DAY_OF_MONTH = 3; @@ -182,158 +209,225 @@ protected static final int YEAR = 6; protected static final int ALL_SPEC_INT = 99; // '*' protected static final int NO_SPEC_INT = 98; // '?' - protected static final Integer ALL_SPEC = new Integer(ALL_SPEC_INT); - protected static final Integer NO_SPEC = new Integer(NO_SPEC_INT); + protected static final Integer ALL_SPEC = ALL_SPEC_INT; + protected static final Integer NO_SPEC = NO_SPEC_INT; - protected static Map monthMap = new HashMap(20); - protected static Map dayMap = new HashMap(60); + protected static final Map monthMap = new HashMap(20); + protected static final Map dayMap = new HashMap(60); static { - monthMap.put("JAN", new Integer(0)); - monthMap.put("FEB", new Integer(1)); - monthMap.put("MAR", new Integer(2)); - monthMap.put("APR", new Integer(3)); - monthMap.put("MAY", new Integer(4)); - monthMap.put("JUN", new Integer(5)); - monthMap.put("JUL", new Integer(6)); - monthMap.put("AUG", new Integer(7)); - monthMap.put("SEP", new Integer(8)); - monthMap.put("OCT", new Integer(9)); - monthMap.put("NOV", new Integer(10)); - monthMap.put("DEC", new Integer(11)); + monthMap.put("JAN", 0); + monthMap.put("FEB", 1); + monthMap.put("MAR", 2); + monthMap.put("APR", 3); + monthMap.put("MAY", 4); + monthMap.put("JUN", 5); + monthMap.put("JUL", 6); + monthMap.put("AUG", 7); + monthMap.put("SEP", 8); + monthMap.put("OCT", 9); + monthMap.put("NOV", 10); + monthMap.put("DEC", 11); - dayMap.put("SUN", new Integer(1)); - dayMap.put("MON", new Integer(2)); - dayMap.put("TUE", new Integer(3)); - dayMap.put("WED", new Integer(4)); - dayMap.put("THU", new Integer(5)); - dayMap.put("FRI", new Integer(6)); - dayMap.put("SAT", new Integer(7)); + dayMap.put("SUN", 1); + dayMap.put("MON", 2); + dayMap.put("TUE", 3); + dayMap.put("WED", 4); + dayMap.put("THU", 5); + dayMap.put("FRI", 6); + dayMap.put("SAT", 7); } - private String cronExpression = null; + private final String cronExpression; private TimeZone timeZone = null; - protected transient TreeSet seconds; - protected transient TreeSet minutes; - protected transient TreeSet hours; - protected transient TreeSet daysOfMonth; - protected transient TreeSet months; - protected transient TreeSet daysOfWeek; - protected transient TreeSet years; + protected transient TreeSet seconds; + protected transient TreeSet minutes; + protected transient TreeSet hours; + protected transient TreeSet daysOfMonth; + protected transient TreeSet months; + protected transient TreeSet daysOfWeek; + protected transient TreeSet years; protected transient boolean lastdayOfWeek = false; protected transient int nthdayOfWeek = 0; protected transient boolean lastdayOfMonth = false; protected transient boolean nearestWeekday = false; - protected transient boolean calendardayOfWeek = false; - protected transient boolean calendardayOfMonth = false; + protected transient int lastdayOffset = 0; protected transient boolean expressionParsed = false; - + + public static final int MAX_YEAR = Calendar.getInstance().get(Calendar.YEAR) + 100; + /** - * Constructs a new 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; - - buildExpression(cronExpression.toUpperCase(Locale.US)); - } - - /** - * 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(); - testDateCal.setTime(date); - testDateCal.set(Calendar.MILLISECOND, 0); - Date originalDate = testDateCal.getTime(); - - testDateCal.add(Calendar.SECOND, -1); - - if (getTimeAfter(testDateCal.getTime()).equals(originalDate)) { - return true; - } else { - return false; - } - } - - /** - * 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); - } - + * Constructs a new 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); + } + /** - *

- * Returns the time zone for which the cronExpression of - * this CronTrigger will be resolved. - *

+ * Constructs a new {@code CronExpression} as a copy of an existing + * instance. + * + * @param expression + * The existing cron expression to be copied */ + public CronExpression(CronExpression expression) { + /* + * We don't call the other constructor here since we need to swallow the + * ParseException. We also elide some of the sanity checking as it is + * not logically trippable. + */ + this.cronExpression = expression.getCronExpression(); + try { + buildExpression(cronExpression); + } catch (ParseException ex) { + throw new AssertionError(); + } + if (expression.getTimeZone() != null) { + setTimeZone((TimeZone) expression.getTimeZone().clone()); + } + } + + /** + * 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; + + //FUTURE_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); + if(newDate == null) + break; + + 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(); + if (timeZone == null) { + timeZone = TimeZone.getDefault(); + } return timeZone; } /** - *

- * Sets the time zone for which the cronExpression of this - * CronTrigger will be resolved. - *

+ * 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; - } - + + /** + * Returns the string representation of the CronExpression + * + * @return a string representation of the CronExpression + */ + @Override + 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; + } + + public static void validateExpression(String cronExpression) throws ParseException { + + new CronExpression(cronExpression); + } + + //////////////////////////////////////////////////////////////////////////// // // Expression Parsing Functions @@ -345,13 +439,27 @@ 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(); + 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; @@ -360,6 +468,19 @@ 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.contains(",")) { + 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.contains(",")) { + throw new ParseException("Support for specifying 'L' with other days of the week is not implemented", -1); + } + if(exprOn == DAY_OF_WEEK && expr.indexOf('#') != -1 && expr.indexOf('#', expr.indexOf('#') +1) != -1) { + throw new ParseException("Support for specifying multiple \"nth\" days is not implemented.", -1); + } + StringTokenizer vTok = new StringTokenizer(expr, ","); while (vTok.hasMoreTokens()) { String v = vTok.nextToken(); @@ -369,12 +490,28 @@ exprOn++; } - if (exprOn <= DAY_OF_WEEK) - throw new ParseException("Unexpected end of expression.", + if (exprOn <= DAY_OF_WEEK) { + throw new ParseException("Unexpected end of expression.", expression.length()); + } - if (exprOn <= YEAR) storeExpressionVals(0, "*", YEAR); + 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) { + if (!dayOfWSpec || dayOfMSpec) { + 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) { @@ -384,57 +521,58 @@ } protected int storeExpressionVals(int pos, String s, int type) - throws ParseException { + throws ParseException { + int incr = 0; int i = skipWhiteSpace(pos, s); - if (i >= s.length()) return i; + if (i >= s.length()) { + return i; + } char c = s.charAt(i); - if ((c >= 'A') && (c <= 'Z') && (!s.equals("L")) && (!s.equals("LW"))) { + if ((c >= 'A') && (c <= 'Z') && (!s.equals("L")) && (!s.equals("LW")) && (!s.matches("^L-[0-9]*[W]?"))) { 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 (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); + 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: '" + 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) + if (eval < 0) { throw new ParseException( "Invalid Day-of-Week value: '" + sub + "'", i); - if (sval > eval) - throw new ParseException( - "Invalid Day-of-Week sequence: " + sval - + " > " + eval, i); - + } } else if (c == '#') { try { i += 4; nthdayOfWeek = Integer.parseInt(s.substring(i)); - if (nthdayOfWeek < 1 || nthdayOfWeek > 5) - throw new Exception(); + 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", @@ -451,27 +589,32 @@ "Illegal characters for this position: '" + sub + "'", i); } - if (eval != -1) incr = 1; + 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 '?': " + 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( + } + 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( + int val = daysOfMonth.last(); + 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); @@ -484,51 +627,73 @@ 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++; + .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); + 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 + 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) { + 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++; + if(c == '-') { + ValueSet vs = getValue(0, s, i+1); + lastdayOffset = vs.value; + if(lastdayOffset > 30) + throw new ParseException("Offset from last day must be <= 30", i+1); + i = vs.pos; + } + if(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 { + 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); @@ -538,14 +703,16 @@ i = checkNext(i, s, val, type); return i; } - } else + } else { throw new ParseException("Unexpected character: " + c, i); + } return i; } protected int checkNext(int pos, String s, int val, int type) - throws ParseException { + throws ParseException { + int end = -1; int i = pos; @@ -557,60 +724,55 @@ 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)); + if (type == DAY_OF_WEEK) { + if(val < 1 || val > 7) + throw new ParseException("Day-of-Week values must be between 1 and 7", -1); + lastdayOfWeek = true; + } else { + throw new ParseException("'L' option is not valid here. (pos=" + i + ")", i); + } + TreeSet set = getSet(type); + set.add(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)); + if (type == DAY_OF_MONTH) { + nearestWeekday = true; + } else { + throw new ParseException("'W' option is not valid here. (pos=" + i + ")", i); + } + if(val > 31) + throw new ParseException("The 'W' option does not make sense with values larger than 31 (max number of days in a month)", i); + TreeSet set = getSet(type); + set.add(val); i++; return i; } if (c == '#') { - if (type != DAY_OF_WEEK) - throw new ParseException( - "'#' option is not valid here. (pos=" + i + ")", i); + 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(); + 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)); + TreeSet set = getSet(type); + set.add(val); i++; return i; } - if (c == 'C') { - if (type == DAY_OF_WEEK) calendardayOfWeek = true; - else if (type == DAY_OF_MONTH) calendardayOfMonth = true; - else - throw new ParseException("'C' option is not valid here. (pos=" - + i + ")", i); - TreeSet set = getSet(type); - set.add(new Integer(val)); - i++; - return i; - } - if (c == '-') { i++; c = s.charAt(i); @@ -624,8 +786,7 @@ c = s.charAt(i); if (c >= '0' && c <= '9') { ValueSet vs = getValue(v, s, i); - int v1 = vs.value; - end = v1; + end = vs.value; i = vs.pos; } if (i < s.length() && ((c = s.charAt(i)) == '/')) { @@ -670,9 +831,9 @@ addToSet(val, end, v3, type); i = vs.pos; return i; - } else - throw new ParseException("Unexpected character '" + c - + "' after '/'", i); + } else { + throw new ParseException("Unexpected character '" + c + "' after '/'", i); + } } addToSet(val, end, 0, type); @@ -681,11 +842,11 @@ } public String getCronExpression() { - return cronExpression; + return cronExpression; } public String getExpressionSummary() { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); buf.append("seconds: "); buf.append(getExpressionSetSummary(seconds)); @@ -717,52 +878,58 @@ buf.append("lastdayOfMonth: "); buf.append(lastdayOfMonth); buf.append("\n"); - buf.append("calendardayOfWeek: "); - buf.append(calendardayOfWeek); - buf.append("\n"); - buf.append("calendardayOfMonth: "); - buf.append(calendardayOfMonth); - buf.append("\n"); buf.append("years: "); buf.append(getExpressionSetSummary(years)); buf.append("\n"); return buf.toString(); } - protected String getExpressionSetSummary(java.util.Set set) { + protected String getExpressionSetSummary(java.util.Set set) { - if (set.contains(NO_SPEC)) return "?"; - if (set.contains(ALL_SPEC)) return "*"; + if (set.contains(NO_SPEC)) { + return "?"; + } + if (set.contains(ALL_SPEC)) { + return "*"; + } - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); - Iterator itr = set.iterator(); + Iterator itr = set.iterator(); boolean first = true; while (itr.hasNext()) { - Integer iVal = (Integer) itr.next(); + Integer iVal = itr.next(); String val = iVal.toString(); - if (!first) buf.append(","); + if (!first) { + buf.append(","); + } buf.append(val); first = false; } return buf.toString(); } - protected String getExpressionSetSummary(java.util.ArrayList list) { + protected String getExpressionSetSummary(java.util.ArrayList list) { - if (list.contains(NO_SPEC)) return "?"; - if (list.contains(ALL_SPEC)) return "*"; + if (list.contains(NO_SPEC)) { + return "?"; + } + if (list.contains(ALL_SPEC)) { + return "*"; + } - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); - Iterator itr = list.iterator(); + Iterator itr = list.iterator(); boolean first = true; while (itr.hasNext()) { - Integer iVal = (Integer) itr.next(); + Integer iVal = itr.next(); String val = iVal.toString(); - if (!first) buf.append(","); + if (!first) { + buf.append(","); + } buf.append(val); first = false; } @@ -771,52 +938,63 @@ } protected int skipWhiteSpace(int i, String s) { - for (; i < s.length() && (s.charAt(i) == ' ' || s.charAt(i) == '\t'); i++) + 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++) + 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); + 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); + 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); + 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); + 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); + 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); + && (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 + if (val != -1) { + set.add(val); + } else { set.add(NO_SPEC); + } + return; } @@ -829,64 +1007,121 @@ } if (type == SECOND || type == MINUTE) { - if (stopAt == -1) stopAt = 59; - if (startAt == -1 || startAt == ALL_SPEC_INT) startAt = 0; + 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; + 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; + 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; + 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; + 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 (stopAt == -1) { + stopAt = MAX_YEAR; + } + if (startAt == -1 || startAt == ALL_SPEC_INT) { + startAt = 1970; + } } - for (int i = startAt; i <= stopAt; i += incr) - set.add(new Integer(i)); + // 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(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(i2); + } + } } - protected TreeSet getSet(int type) { + 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; + 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); + StringBuilder s1 = new StringBuilder(String.valueOf(v)); while (c >= '0' && c <= '9') { - s1 += c; + s1.append(c); i++; - if (i >= s.length()) break; + if (i >= s.length()) { + break; + } c = s.charAt(i); } ValueSet val = new ValueSet(); - if (i < s.length()) val.pos = i; - else - val.pos = i + 1; - val.value = Integer.parseInt(s1); + + val.pos = (i < s.length()) ? i : i + 1; + val.value = Integer.parseInt(s1.toString()); return val; } @@ -897,48 +1132,35 @@ } protected int getMonthNumber(String s) { - Integer integer = (Integer) monthMap.get(s); + Integer integer = monthMap.get(s); - if (integer == null) return -1; + if (integer == null) { + return -1; + } - return integer.intValue(); + return integer; } protected int getDayOfWeekNumber(String s) { - Integer integer = (Integer) dayMap.get(s); + Integer integer = dayMap.get(s); - if (integer == null) return -1; + if (integer == null) { + return -1; + } - return integer.intValue(); + return integer; } - protected Date getTime(int sc, int mn, int hr, int dayofmn, int mon) { - try { - Calendar cl = Calendar.getInstance(getTimeZone()); - //cl.add(Calendar.DAY_OF_MONTH,); - if (hr >= 0 && hr <= 12) cl.set(Calendar.AM_PM, Calendar.AM); - if (hr >= 13 && hr <= 23) cl.set(Calendar.AM_PM, Calendar.PM); - cl.setLenient(false); - if (sc != -1) cl.set(Calendar.SECOND, sc); - if (mn != -1) cl.set(Calendar.MINUTE, mn); - if (hr != -1) cl.set(Calendar.HOUR_OF_DAY, hr); - if (dayofmn != -1) cl.set(Calendar.DAY_OF_MONTH, dayofmn); - if (mon != -1) cl.set(Calendar.MONTH, mon); - return cl.getTime(); - } catch (Exception e) { - return null; - } - } - //////////////////////////////////////////////////////////////////////////// // // Computation Functions // //////////////////////////////////////////////////////////////////////////// - protected Date getTimeAfter(Date afterTime) { + public Date getTimeAfter(Date afterTime) { - Calendar cl = Calendar.getInstance(getTimeZone()); + // Computation is based on Gregorian year only. + Calendar cl = new java.util.GregorianCalendar(getTimeZone()); // move ahead one second, since we're computing the time *after* the // given time @@ -952,19 +1174,22 @@ 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; + 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)); + st = seconds.tailSet(sec); if (st != null && st.size() != 0) { - sec = ((Integer) st.first()).intValue(); + sec = st.first(); } else { - sec = ((Integer) seconds.first()).intValue(); + sec = seconds.first(); min++; cl.set(Calendar.MINUTE, min); } @@ -975,12 +1200,12 @@ t = -1; // get minute................................................. - st = minutes.tailSet(new Integer(min)); + st = minutes.tailSet(min); if (st != null && st.size() != 0) { t = min; - min = ((Integer) st.first()).intValue(); + min = st.first(); } else { - min = ((Integer) minutes.first()).intValue(); + min = minutes.first(); hr++; } if (min != t) { @@ -996,12 +1221,12 @@ t = -1; // get hour................................................... - st = hours.tailSet(new Integer(hr)); + st = hours.tailSet(hr); if (st != null && st.size() != 0) { t = hr; - hr = ((Integer) st.first()).intValue(); + hr = st.first(); } else { - hr = ((Integer) hours.first()).intValue(); + hr = hours.first(); day++; } if (hr != t) { @@ -1024,17 +1249,27 @@ 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)); + st = daysOfMonth.tailSet(day); if (lastdayOfMonth) { if(!nearestWeekday) { t = day; day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); - } - else { + day -= lastdayOffset; + if(t > day) { + mon++; + if(mon > 12) { + mon = 1; + tmon = 3333; // ensure test of mon != tmon further below fails + cl.add(Calendar.YEAR, 1); + } + day = 1; + } + } else { t = day; day = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); + day -= lastdayOffset; - java.util.Calendar tcal = java.util.Calendar.getInstance(); + 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); @@ -1045,14 +1280,15 @@ int ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); int dow = tcal.get(Calendar.DAY_OF_WEEK); - if(dow == Calendar.SATURDAY && day == 1) + if(dow == Calendar.SATURDAY && day == 1) { day += 2; - else if(dow == Calendar.SATURDAY) + } else if(dow == Calendar.SATURDAY) { day -= 1; - else if(dow == Calendar.SUNDAY && day == ldom) + } else if(dow == Calendar.SUNDAY && day == ldom) { day -= 2; - else if(dow == Calendar.SUNDAY) + } else if(dow == Calendar.SUNDAY) { day += 1; + } tcal.set(Calendar.SECOND, sec); tcal.set(Calendar.MINUTE, min); @@ -1067,9 +1303,9 @@ } } else if(nearestWeekday) { t = day; - day = ((Integer) daysOfMonth.first()).intValue(); + day = daysOfMonth.first(); - java.util.Calendar tcal = java.util.Calendar.getInstance(); + 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); @@ -1080,14 +1316,15 @@ int ldom = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); int dow = tcal.get(Calendar.DAY_OF_WEEK); - if(dow == Calendar.SATURDAY && day == 1) + if(dow == Calendar.SATURDAY && day == 1) { day += 2; - else if(dow == Calendar.SATURDAY) + } else if(dow == Calendar.SATURDAY) { day -= 1; - else if(dow == Calendar.SUNDAY && day == ldom) + } else if(dow == Calendar.SUNDAY && day == ldom) { day -= 2; - else if(dow == Calendar.SUNDAY) + } else if(dow == Calendar.SUNDAY) { day += 1; + } tcal.set(Calendar.SECOND, sec); @@ -1097,15 +1334,20 @@ tcal.set(Calendar.MONTH, mon - 1); Date nTime = tcal.getTime(); if(nTime.before(afterTime)) { - day = ((Integer) daysOfMonth.first()).intValue();; + day = daysOfMonth.first(); mon++; } - } - else if (st != null && st.size() != 0) { + } else if (st != null && st.size() != 0) { t = day; - day = ((Integer) st.first()).intValue(); + day = st.first(); + // 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 = daysOfMonth.first(); + mon++; + } } else { - day = ((Integer) daysOfMonth.first()).intValue(); + day = daysOfMonth.first(); mon++; } @@ -1122,12 +1364,16 @@ } 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 + int dow = daysOfWeek.first(); // 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); + if (cDow < dow) { + daysToAdd = dow - cDow; + } + if (cDow > dow) { + daysToAdd = dow + (7 - cDow); + } int lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); @@ -1142,9 +1388,10 @@ continue; } - // find date of last occurance of this day in this month... - while ((day + daysToAdd + 7) <= lDay) + // find date of last occurrence of this day in this month... + while ((day + daysToAdd + 7) <= lDay) { daysToAdd += 7; + } day += daysToAdd; @@ -1160,19 +1407,26 @@ } else if (nthdayOfWeek != 0) { // are we looking for the Nth XXX day in the month? - int dow = ((Integer) daysOfWeek.first()).intValue(); // desired + int dow = daysOfWeek.first(); // 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); + if (cDow < dow) { + daysToAdd = dow - cDow; + } else if (cDow > dow) { + daysToAdd = dow + (7 - cDow); + } boolean dayShifted = false; - if (daysToAdd > 0) dayShifted = true; + if (daysToAdd > 0) { + dayShifted = true; + } day += daysToAdd; int weekOfMonth = day / 7; - if (day % 7 > 0) weekOfMonth++; + if (day % 7 > 0) { + weekOfMonth++; + } daysToAdd = (nthdayOfWeek - weekOfMonth) * 7; day += daysToAdd; @@ -1197,16 +1451,20 @@ } } else { int cDow = cl.get(Calendar.DAY_OF_WEEK); // current d-o-w - int dow = ((Integer) daysOfWeek.first()).intValue(); // desired + int dow = daysOfWeek.first(); // desired // d-o-w - st = daysOfWeek.tailSet(new Integer(cDow)); + st = daysOfWeek.tailSet(cDow); if (st != null && st.size() > 0) { - dow = ((Integer) st.first()).intValue(); + dow = st.first(); } int daysToAdd = 0; - if (cDow < dow) daysToAdd = dow - cDow; - if (cDow > dow) daysToAdd = dow + (7 - cDow); + if (cDow < dow) { + daysToAdd = dow - cDow; + } + if (cDow > dow) { + daysToAdd = dow + (7 - cDow); + } int lDay = getLastDayOfMonth(mon, cl.get(Calendar.YEAR)); @@ -1233,7 +1491,6 @@ } 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); @@ -1245,15 +1502,17 @@ // test for expressions that never generate a valid fire date, // but keep looping... - if (year > 2099) return null; + if (year > MAX_YEAR) { + return null; + } // get month................................................... - st = months.tailSet(new Integer(mon)); + st = months.tailSet(mon); if (st != null && st.size() != 0) { t = mon; - mon = ((Integer) st.first()).intValue(); + mon = st.first(); } else { - mon = ((Integer) months.first()).intValue(); + mon = months.first(); year++; } if (mon != t) { @@ -1275,12 +1534,13 @@ t = -1; // get year................................................... - st = years.tailSet(new Integer(year)); + st = years.tailSet(year); if (st != null && st.size() != 0) { t = year; - year = ((Integer) st.first()).intValue(); - } else + year = st.first(); + } else { return null; // ran out of years... + } if (year != t) { cl.set(Calendar.SECOND, 0); @@ -1305,8 +1565,8 @@ * Advance the calendar to the particular hour paying particular attention * to daylight saving problems. * - * @param cal - * @param hour + * @param cal the calendar to operate on + * @param hour the hour to set */ protected void setCalendarHour(Calendar cal, int hour) { cal.set(java.util.Calendar.HOUR_OF_DAY, hour); @@ -1315,74 +1575,81 @@ } } - protected Date getTimeBefore(Date endTime) // TODO: implement - { + /** + * NOT YET IMPLEMENTED: Returns the time before the given time + * that the CronExpression matches. + */ + public Date getTimeBefore(Date endTime) { + // FUTURE_TODO: implement QUARTZ-423 return null; } + /** + * NOT YET IMPLEMENTED: Returns the final time that the + * CronExpression will match. + */ + public Date getFinalFireTime() { + // FUTURE_TODO: implement QUARTZ-423 + return null; + } + protected boolean isLeapYear(int year) { - if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) return true; - else - return false; + 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); + 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 { + throws java.io.IOException, ClassNotFoundException { + stream.defaultReadObject(); try { buildExpression(cronExpression); } catch (Exception ignore) { } // never happens } + @Override + @Deprecated 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; - } + return new CronExpression(this); + } } class ValueSet { public int value; public int pos; -} \ No newline at end of file +} Index: 3rdParty_sources/quartz/org/quartz/CronScheduleBuilder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/CronScheduleBuilder.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/CronScheduleBuilder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,328 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.text.ParseException; +import java.util.TimeZone; + +import org.quartz.impl.triggers.CronTriggerImpl; +import org.quartz.spi.MutableTrigger; + +/** + * CronScheduleBuilder is a {@link ScheduleBuilder} that defines + * {@link CronExpression}-based schedules for Triggers. + * + *

+ * Quartz provides a builder-style API for constructing scheduling-related + * entities via a Domain-Specific Language (DSL). The DSL can best be utilized + * through the usage of static imports of the methods on the classes + * TriggerBuilder, JobBuilder, + * DateBuilder, JobKey, TriggerKey and + * the various ScheduleBuilder implementations. + *

+ * + *

+ * Client code can then use the DSL to write code such as this: + *

+ * + *
+ * JobDetail job = newJob(MyJob.class).withIdentity("myJob").build();
+ * 
+ * Trigger trigger = newTrigger()
+ *         .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+ *         .withSchedule(dailyAtHourAndMinute(10, 0))
+ *         .startAt(futureDate(10, MINUTES)).build();
+ * 
+ * scheduler.scheduleJob(job, trigger);
+ * 
+ * 
+ * 
+ * @see CronExpression
+ * @see CronTrigger
+ * @see ScheduleBuilder
+ * @see SimpleScheduleBuilder 
+ * @see CalendarIntervalScheduleBuilder
+ * @see TriggerBuilder
+ */
+public class CronScheduleBuilder extends ScheduleBuilder {
+
+    private CronExpression cronExpression;
+    private int misfireInstruction = CronTrigger.MISFIRE_INSTRUCTION_SMART_POLICY;
+
+    protected CronScheduleBuilder(CronExpression cronExpression) {
+        if (cronExpression == null) {
+            throw new NullPointerException("cronExpression cannot be null");
+        }
+        this.cronExpression = cronExpression;
+    }
+
+    /**
+     * Build the actual Trigger -- NOT intended to be invoked by end users, but
+     * will rather be invoked by a TriggerBuilder which this ScheduleBuilder is
+     * given to.
+     * 
+     * @see TriggerBuilder#withSchedule(ScheduleBuilder)
+     */
+    @Override
+    public MutableTrigger build() {
+
+        CronTriggerImpl ct = new CronTriggerImpl();
+
+        ct.setCronExpression(cronExpression);
+        ct.setTimeZone(cronExpression.getTimeZone());
+        ct.setMisfireInstruction(misfireInstruction);
+
+        return ct;
+    }
+
+    /**
+     * Create a CronScheduleBuilder with the given cron-expression string -
+     * which is presumed to b e valid cron expression (and hence only a
+     * RuntimeException will be thrown if it is not).
+     * 
+     * @param cronExpression
+     *            the cron expression string to base the schedule on.
+     * @return the new CronScheduleBuilder
+     * @throws RuntimeException
+     *             wrapping a ParseException if the expression is invalid
+     * @see CronExpression
+     */
+    public static CronScheduleBuilder cronSchedule(String cronExpression) {
+        try {
+            return cronSchedule(new CronExpression(cronExpression));
+        } catch (ParseException e) {
+            // all methods of construction ensure the expression is valid by
+            // this point...
+            throw new RuntimeException("CronExpression '" + cronExpression
+                    + "' is invalid.", e);
+        }
+    }
+
+    /**
+     * Create a CronScheduleBuilder with the given cron-expression string -
+     * which may not be a valid cron expression (and hence a ParseException will
+     * be thrown if it is not).
+     * 
+     * @param cronExpression
+     *            the cron expression string to base the schedule on.
+     * @return the new CronScheduleBuilder
+     * @throws ParseException
+     *             if the expression is invalid
+     * @see CronExpression
+     */
+    public static CronScheduleBuilder cronScheduleNonvalidatedExpression(
+            String cronExpression) throws ParseException {
+        return cronSchedule(new CronExpression(cronExpression));
+    }
+
+    private static CronScheduleBuilder cronScheduleNoParseException(
+            String presumedValidCronExpression) {
+        try {
+            return cronSchedule(new CronExpression(presumedValidCronExpression));
+        } catch (ParseException e) {
+            // all methods of construction ensure the expression is valid by
+            // this point...
+            throw new RuntimeException(
+                    "CronExpression '"
+                            + presumedValidCronExpression
+                            + "' is invalid, which should not be possible, please report bug to Quartz developers.",
+                    e);
+        }
+    }
+
+    /**
+     * Create a CronScheduleBuilder with the given cron-expression.
+     * 
+     * @param cronExpression
+     *            the cron expression to base the schedule on.
+     * @return the new CronScheduleBuilder
+     * @see CronExpression
+     */
+    public static CronScheduleBuilder cronSchedule(CronExpression cronExpression) {
+        return new CronScheduleBuilder(cronExpression);
+    }
+
+    /**
+     * Create a CronScheduleBuilder with a cron-expression that sets the
+     * schedule to fire every day at the given time (hour and minute).
+     * 
+     * @param hour
+     *            the hour of day to fire
+     * @param minute
+     *            the minute of the given hour to fire
+     * @return the new CronScheduleBuilder
+     * @see CronExpression
+     */
+    public static CronScheduleBuilder dailyAtHourAndMinute(int hour, int minute) {
+        DateBuilder.validateHour(hour);
+        DateBuilder.validateMinute(minute);
+
+        String cronExpression = String.format("0 %d %d ? * *", minute, hour);
+
+        return cronScheduleNoParseException(cronExpression);
+    }
+
+    /**
+     * Create a CronScheduleBuilder with a cron-expression that sets the
+     * schedule to fire at the given day at the given time (hour and minute) on
+     * the given days of the week.
+     * 
+     * @param daysOfWeek
+     *            the dasy of the week to fire
+     * @param hour
+     *            the hour of day to fire
+     * @param minute
+     *            the minute of the given hour to fire
+     * @return the new CronScheduleBuilder
+     * @see CronExpression
+     * @see DateBuilder#MONDAY
+     * @see DateBuilder#TUESDAY
+     * @see DateBuilder#WEDNESDAY
+     * @see DateBuilder#THURSDAY
+     * @see DateBuilder#FRIDAY
+     * @see DateBuilder#SATURDAY
+     * @see DateBuilder#SUNDAY
+     */
+
+    public static CronScheduleBuilder atHourAndMinuteOnGivenDaysOfWeek(
+            int hour, int minute, Integer... daysOfWeek) {
+        if (daysOfWeek == null || daysOfWeek.length == 0)
+            throw new IllegalArgumentException(
+                    "You must specify at least one day of week.");
+        for (int dayOfWeek : daysOfWeek)
+            DateBuilder.validateDayOfWeek(dayOfWeek);
+        DateBuilder.validateHour(hour);
+        DateBuilder.validateMinute(minute);
+
+        String cronExpression = String.format("0 %d %d ? * %d", minute, hour,
+                daysOfWeek[0]);
+
+        for (int i = 1; i < daysOfWeek.length; i++)
+            cronExpression = cronExpression + "," + daysOfWeek[i];
+
+        return cronScheduleNoParseException(cronExpression);
+    }
+
+    /**
+     * Create a CronScheduleBuilder with a cron-expression that sets the
+     * schedule to fire one per week on the given day at the given time (hour
+     * and minute).
+     * 
+     * @param dayOfWeek
+     *            the day of the week to fire
+     * @param hour
+     *            the hour of day to fire
+     * @param minute
+     *            the minute of the given hour to fire
+     * @return the new CronScheduleBuilder
+     * @see CronExpression
+     * @see DateBuilder#MONDAY
+     * @see DateBuilder#TUESDAY
+     * @see DateBuilder#WEDNESDAY
+     * @see DateBuilder#THURSDAY
+     * @see DateBuilder#FRIDAY
+     * @see DateBuilder#SATURDAY
+     * @see DateBuilder#SUNDAY
+     */
+    public static CronScheduleBuilder weeklyOnDayAndHourAndMinute(
+            int dayOfWeek, int hour, int minute) {
+        DateBuilder.validateDayOfWeek(dayOfWeek);
+        DateBuilder.validateHour(hour);
+        DateBuilder.validateMinute(minute);
+
+        String cronExpression = String.format("0 %d %d ? * %d", minute, hour,
+                dayOfWeek);
+
+        return cronScheduleNoParseException(cronExpression);
+    }
+
+    /**
+     * Create a CronScheduleBuilder with a cron-expression that sets the
+     * schedule to fire one per month on the given day of month at the given
+     * time (hour and minute).
+     * 
+     * @param dayOfMonth
+     *            the day of the month to fire
+     * @param hour
+     *            the hour of day to fire
+     * @param minute
+     *            the minute of the given hour to fire
+     * @return the new CronScheduleBuilder
+     * @see CronExpression
+     */
+    public static CronScheduleBuilder monthlyOnDayAndHourAndMinute(
+            int dayOfMonth, int hour, int minute) {
+        DateBuilder.validateDayOfMonth(dayOfMonth);
+        DateBuilder.validateHour(hour);
+        DateBuilder.validateMinute(minute);
+
+        String cronExpression = String.format("0 %d %d %d * ?", minute, hour,
+                dayOfMonth);
+
+        return cronScheduleNoParseException(cronExpression);
+    }
+
+    /**
+     * The TimeZone in which to base the schedule.
+     * 
+     * @param timezone
+     *            the time-zone for the schedule.
+     * @return the updated CronScheduleBuilder
+     * @see CronExpression#getTimeZone()
+     */
+    public CronScheduleBuilder inTimeZone(TimeZone timezone) {
+        cronExpression.setTimeZone(timezone);
+        return this;
+    }
+
+    /**
+     * If the Trigger misfires, use the
+     * {@link Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY} instruction.
+     * 
+     * @return the updated CronScheduleBuilder
+     * @see Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
+     */
+    public CronScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
+        misfireInstruction = Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY;
+        return this;
+    }
+
+    /**
+     * If the Trigger misfires, use the
+     * {@link CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING} instruction.
+     * 
+     * @return the updated CronScheduleBuilder
+     * @see CronTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
+     */
+    public CronScheduleBuilder withMisfireHandlingInstructionDoNothing() {
+        misfireInstruction = CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING;
+        return this;
+    }
+
+    /**
+     * If the Trigger misfires, use the
+     * {@link CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW} instruction.
+     * 
+     * @return the updated CronScheduleBuilder
+     * @see CronTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
+     */
+    public CronScheduleBuilder withMisfireHandlingInstructionFireAndProceed() {
+        misfireInstruction = CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
+        return this;
+    }
+}
Index: 3rdParty_sources/quartz/org/quartz/CronTrigger.java
===================================================================
diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b
--- 3rdParty_sources/quartz/org/quartz/CronTrigger.java	(.../CronTrigger.java)	(revision 2e3463e873227c6a3edcb3e02d55270219e553ff)
+++ 3rdParty_sources/quartz/org/quartz/CronTrigger.java	(.../CronTrigger.java)	(revision c208628989d52041b3765784f4c8cbfd6c80d47b)
@@ -1,5 +1,5 @@
-/* 
- * Copyright 2004-2005 OpenSymphony 
+/*
+ * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.
  * 
  * 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 
@@ -15,22 +15,15 @@
  * 
  */
 
-/*
- * Previously Copyright (c) 2001-2004 James House
- */
 package 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 org.quartz.JobDetail} - * at given moments in time, defined with Unix 'cron-like' definitions. - *

+ * The public interface for inspecting settings specific to a CronTrigger, . + * which is used to fire a {@link org.quartz.Job} + * at given moments in time, defined with Unix 'cron-like' schedule definitions. * *

* For those unfamiliar with "cron", this means being able to create a firing @@ -139,7 +132,7 @@ * * "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 + * Fire at 10:15am on every last Friday of every month during the years 2002, 2003, 2004 and 2005 * * * @@ -168,23 +161,16 @@ * *

* - * @see Trigger - * @see SimpleTrigger - * @see TriggerUtils + * @see CronScheduleBuilder + * @see TriggerBuilder * - * @author Sharada Jambula, James House + * @author jhouse * @author Contributions from Mads Henderson */ -public class CronTrigger extends Trigger { +public interface CronTrigger extends Trigger { - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Constants. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - + public static final long serialVersionUID = -8644953146451592766L; + /** *

* Instructs the {@link Scheduler} that upon a mis-fire @@ -193,7 +179,7 @@ *

*/ public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1; - + /** *

* Instructs the {@link Scheduler} that upon a mis-fire @@ -205,765 +191,17 @@ */ public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2; - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ + public String getCronExpression(); - private CronExpression cronEx = null; - private Date startTime = null; - private Date endTime = null; - private Date nextFireTime = null; - private Date previousFireTime = null; - private transient TimeZone timeZone = null; - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Constructors. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - /** *

- * 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 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 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 org.quartz.JobDetail}, - * and with the given "cron" expression resolved with respect to the TimeZone. - *

- */ - public CronTrigger(String name, String group, String jobName, - String jobGroup, String cronExpression, TimeZone timeZone) - throws ParseException { - this(name, group, jobName, jobGroup, null, null, cronExpression, - 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 - * A 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. - */ - 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 the Trigger - * @param group - * of the Trigger - * @param jobName, - * name of the {@link org.quartz.JobDetail} - * executed on firetime - * @param jobGroup, - * group of the {@link 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(); - copy.setCronExpression((CronExpression)cronEx.clone()); - return copy; - } - - public void setCronExpression(String cronExpression) throws ParseException { - this.cronEx = new CronExpression(cronExpression); - this.cronEx.setTimeZone(getTimeZone()); - } - - public String getCronExpression() { - return cronEx == null ? null : cronEx.getCronExpression(); - } - - public void setCronExpression(CronExpression cronExpression) { - this.cronEx = cronExpression; - this.timeZone = cronExpression.getTimeZone(); - } - - /** - *

- * Get the time at which the CronTrigger should occur. - *

- */ - public Date getStartTime() { - return this.startTime; - } - - 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"); - - // round off millisecond... - // Note timeZone is not needed here as parameter for - // Calendar.getInstance(), - // since time zone is implicit when using a Date in the setTime method. - Calendar cl = Calendar.getInstance(); - cl.setTime(startTime); - cl.set(Calendar.MILLISECOND, 0); - - this.startTime = cl.getTime(); - } - - /** - *

- * Get the time at which the CronTrigger should quit - * repeating - even if repeastCount isn't yet satisfied. - *

- * - * @see #getFinalFireTime() - */ - public Date getEndTime() { - return this.endTime; - } - - public void setEndTime(Date endTime) { - Date sTime = getStartTime(); - if (sTime != null && endTime != null && sTime.after(endTime)) - throw new IllegalArgumentException( - "End time cannot be before start time"); - - this.endTime = endTime; - } - - /** - *

- * Returns the next time at which the CronTrigger will fire. - * If the trigger will not fire again, null will be - * returned. The value returned is not guaranteed to be valid until after - * the Trigger has been added to the scheduler. - *

- */ - public Date getNextFireTime() { - return this.nextFireTime; - } - - /** - *

- * Returns the previous time at which the CronTrigger will - * fire. 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. - *

- */ - 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 time zone for which the cronExpression of * this CronTrigger will be resolved. *

*/ - public TimeZone getTimeZone() { - - if(cronEx != null) return cronEx.getTimeZone(); - - if (timeZone == null) timeZone = TimeZone.getDefault(); - return timeZone; - } + public TimeZone getTimeZone(); - /** - *

- * Sets the time zone for which the cronExpression of this - * CronTrigger will be resolved. - *

- */ - public void setTimeZone(TimeZone timeZone) { - if(cronEx != null) cronEx.setTimeZone(timeZone); - this.timeZone = timeZone; - } + public String getExpressionSummary(); - /** - *

- * 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 - * org.quartz.Calendar (if any) - *

- */ - public Date getFireTimeAfter(Date afterTime) { - if (afterTime == null) afterTime = new Date(); - - if (startTime.after(afterTime)) - afterTime = new Date(startTime.getTime() - 1000l); - - Date pot = getTimeAfter(afterTime); - if (endTime != null && pot != null && pot.after(endTime)) return null; - - return pot; - } - - /** - *

- * 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 org.quartz.calendar - *

- */ - public Date getFinalFireTime() { - if (this.endTime != null) return getTimeBefore(this.endTime); - else - return null; - } - - /** - *

- * Determines whether or not the CronTrigger will occur - * again. - *

- */ - public boolean mayFireAgain() { - return (getNextFireTime() != null); - } - - protected boolean validateMisfireInstruction(int misfireInstruction) { - if (misfireInstruction < MISFIRE_INSTRUCTION_SMART_POLICY) - return false; - - if (misfireInstruction > MISFIRE_INSTRUCTION_DO_NOTHING) return false; - - return true; - } - - /** - *

- * Updates the CronTrigger'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:
- *

    - *
  • The instruction will be interpreted as MISFIRE_INSTRUCTION_FIRE_ONCE_NOW - *
- *

- */ - public void updateAfterMisfire(org.quartz.Calendar cal) { - int instr = getMisfireInstruction(); - - if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) - instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW; - - if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) { - Date newFireTime = getFireTimeAfter(new Date()); - while (newFireTime != null && cal != null - && !cal.isTimeIncluded(newFireTime.getTime())) { - newFireTime = getFireTimeAfter(newFireTime); - } - setNextFireTime(newFireTime); - } else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) { - setNextFireTime(new Date()); - } - } - - /** - *

- * 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). - *

- * - * @param test the date to compare - * - * @see #willFireOn(Calendar, boolean) - */ - public boolean willFireOn(Calendar test) { - return willFireOn(test, 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 - * 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 org.quartz.JobDetail} associated with the Trigger - * in order to get the final instruction code from the trigger. - *

- * - * @param context - * is the 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). - *

- * - * @see #executionComplete(JobExecutionContext, JobExecutionException) - */ - public void triggered(org.quartz.Calendar calendar) { - previousFireTime = nextFireTime; - nextFireTime = getFireTimeAfter(nextFireTime); - - while (nextFireTime != null && calendar != null - && !calendar.isTimeIncluded(nextFireTime.getTime())) { - nextFireTime = getFireTimeAfter(nextFireTime); - } - } - - /** - * - * @see org.quartz.Trigger#updateWithNewCalendar(org.quartz.Calendar, long) - */ - public void updateWithNewCalendar(org.quartz.Calendar calendar, long misfireThreshold) - { - nextFireTime = getFireTimeAfter(previousFireTime); - - Date now = new Date(); - do { - while (nextFireTime != null && calendar != null - && !calendar.isTimeIncluded(nextFireTime.getTime())) { - nextFireTime = getFireTimeAfter(nextFireTime); - } - - if(nextFireTime != null && nextFireTime.before(now)) { - long diff = now.getTime() - nextFireTime.getTime(); - if(diff >= misfireThreshold) { - nextFireTime = getFireTimeAfter(nextFireTime); - continue; - } - } - }while(false); - } - - /** - *

- * 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. - *

- * - * @return the first time at which the 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(org.quartz.Calendar calendar) { - nextFireTime = getFireTimeAfter(new Date(startTime.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(); - } - - //////////////////////////////////////////////////////////////////////////// - // - // Computation Functions - // - //////////////////////////////////////////////////////////////////////////// - - protected Date getTimeAfter(Date afterTime) { - return cronEx.getTimeAfter(afterTime); - } - - protected Date getTimeBefore(Date endTime) - { - return null; - } - - 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)); -//// } -//// } - - } + TriggerBuilder getTriggerBuilder(); } - Index: 3rdParty_sources/quartz/org/quartz/DailyTimeIntervalScheduleBuilder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/DailyTimeIntervalScheduleBuilder.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/DailyTimeIntervalScheduleBuilder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,413 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import org.quartz.DateBuilder.IntervalUnit; +import org.quartz.impl.triggers.DailyTimeIntervalTriggerImpl; +import org.quartz.spi.MutableTrigger; + +/** + * A {@link ScheduleBuilder} implementation that build schedule for DailyTimeIntervalTrigger. + * + *

This builder provide an extra convenient method for you to set the trigger's endTimeOfDay. You may + * use either endingDailyAt() or endingDailyAfterCount() to set the value. The later will auto calculate + * your endTimeOfDay by using the interval, intervalUnit and startTimeOfDay to perform the calculation. + * + *

When using endingDailyAfterCount(), you should note that it is used to calculating endTimeOfDay. So + * if your startTime on the first day is already pass by a time that would not add up to the count you + * expected, until the next day comes. Remember that DailyTimeIntervalTrigger will use startTimeOfDay + * and endTimeOfDay as fresh per each day! + * + *

Quartz provides a builder-style API for constructing scheduling-related + * entities via a Domain-Specific Language (DSL). The DSL can best be + * utilized through the usage of static imports of the methods on the classes + * TriggerBuilder, JobBuilder, + * DateBuilder, JobKey, TriggerKey + * and the various ScheduleBuilder implementations.

+ * + *

Client code can then use the DSL to write code such as this:

+ *
+ *         JobDetail job = newJob(MyJob.class)
+ *             .withIdentity("myJob")
+ *             .build();
+ *             
+ *         Trigger trigger = newTrigger() 
+ *             .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+ *             .withSchedule(onDaysOfTheWeek(MONDAY, THURSDAY))
+ *             .startAt(futureDate(10, MINUTES))
+ *             .build();
+ *         
+ *         scheduler.scheduleJob(job, trigger);
+ * 
+ *   
+ * @since 2.1.0
+ * 
+ * @author James House
+ * @author Zemian Deng 
+ */
+public class DailyTimeIntervalScheduleBuilder extends ScheduleBuilder {
+
+    private int interval = 1;
+    private IntervalUnit intervalUnit = IntervalUnit.MINUTE;
+    private Set daysOfWeek;
+    private TimeOfDay startTimeOfDay;
+    private TimeOfDay endTimeOfDay;
+    private int repeatCount = DailyTimeIntervalTrigger.REPEAT_INDEFINITELY;
+
+    private int misfireInstruction = CalendarIntervalTrigger.MISFIRE_INSTRUCTION_SMART_POLICY;
+    
+    /**
+     * A set of all days of the week.
+     * 
+     * The set contains all values between {@link java.util.Calendar#SUNDAY} and {@link java.util.Calendar#SATURDAY} 
+     * (the integers from 1 through 7). 
+     */
+    public static final Set ALL_DAYS_OF_THE_WEEK;
+    
+    /** 
+     * A set of the business days of the week (for locales similar to the USA).
+     * 
+     * The set contains all values between {@link java.util.Calendar#MONDAY} and {@link java.util.Calendar#FRIDAY} 
+     * (the integers from 2 through 6). 
+     */
+    public static final Set MONDAY_THROUGH_FRIDAY;
+    
+    /**
+     * A set of the weekend days of the week (for locales similar to the USA).
+     * 
+     * The set contains {@link java.util.Calendar#SATURDAY} and {@link java.util.Calendar#SUNDAY} 
+     */
+    public static final Set SATURDAY_AND_SUNDAY;
+    
+    static {
+        Set t = new HashSet(7);
+        for(int i=Calendar.SUNDAY; i <= Calendar.SATURDAY; i++)
+            t.add(i);
+        ALL_DAYS_OF_THE_WEEK = Collections.unmodifiableSet(t);
+        
+        t = new HashSet(5);
+        for(int i=Calendar.MONDAY; i <= Calendar.FRIDAY; i++)
+            t.add(i);
+        MONDAY_THROUGH_FRIDAY = Collections.unmodifiableSet(t);
+        
+        t = new HashSet(2);
+        t.add(Calendar.SUNDAY);
+        t.add(Calendar.SATURDAY);
+        SATURDAY_AND_SUNDAY = Collections.unmodifiableSet(t);
+    }
+    
+    protected DailyTimeIntervalScheduleBuilder() {
+    }
+    
+    /**
+     * Create a DailyTimeIntervalScheduleBuilder.
+     * 
+     * @return the new DailyTimeIntervalScheduleBuilder
+     */
+    public static DailyTimeIntervalScheduleBuilder dailyTimeIntervalSchedule() {
+        return new DailyTimeIntervalScheduleBuilder();
+    }
+    
+    /**
+     * Build the actual Trigger -- NOT intended to be invoked by end users,
+     * but will rather be invoked by a TriggerBuilder which this 
+     * ScheduleBuilder is given to.
+     * 
+     * @see TriggerBuilder#withSchedule(ScheduleBuilder)
+     */
+    @Override
+    public MutableTrigger build() {
+
+        DailyTimeIntervalTriggerImpl st = new DailyTimeIntervalTriggerImpl();
+        st.setRepeatInterval(interval);
+        st.setRepeatIntervalUnit(intervalUnit);
+        st.setMisfireInstruction(misfireInstruction);
+        st.setRepeatCount(repeatCount);
+        
+        if(daysOfWeek != null)
+            st.setDaysOfWeek(daysOfWeek);
+        else
+            st.setDaysOfWeek(ALL_DAYS_OF_THE_WEEK);
+
+        if(startTimeOfDay != null)
+            st.setStartTimeOfDay(startTimeOfDay);
+        else
+            st.setStartTimeOfDay(TimeOfDay.hourAndMinuteOfDay(0, 0));
+
+        if(endTimeOfDay != null)
+            st.setEndTimeOfDay(endTimeOfDay);
+        else
+            st.setEndTimeOfDay(TimeOfDay.hourMinuteAndSecondOfDay(23, 59, 59));
+        
+        return st;
+    }
+
+    /**
+     * Specify the time unit and interval for the Trigger to be produced.
+     * 
+     * @param timeInterval the interval at which the trigger should repeat.
+     * @param unit the time unit (IntervalUnit) of the interval. The only intervals that are valid for this type of 
+     * trigger are {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}.
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     * @see DailyTimeIntervalTrigger#getRepeatInterval()
+     * @see DailyTimeIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public DailyTimeIntervalScheduleBuilder withInterval(int timeInterval, IntervalUnit unit) {
+        if (unit == null || !(unit.equals(IntervalUnit.SECOND) || 
+                unit.equals(IntervalUnit.MINUTE) ||unit.equals(IntervalUnit.HOUR)))
+            throw new IllegalArgumentException("Invalid repeat IntervalUnit (must be SECOND, MINUTE or HOUR).");
+        validateInterval(timeInterval);
+        this.interval = timeInterval;
+        this.intervalUnit = unit;
+        return this;
+    }
+
+    /**
+     * Specify an interval in the IntervalUnit.SECOND that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInSeconds the number of seconds at which the trigger should repeat.
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     * @see DailyTimeIntervalTrigger#getRepeatInterval()
+     * @see DailyTimeIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public DailyTimeIntervalScheduleBuilder withIntervalInSeconds(int intervalInSeconds) {
+        withInterval(intervalInSeconds, IntervalUnit.SECOND);
+        return this;
+    }
+    
+    /**
+     * Specify an interval in the IntervalUnit.MINUTE that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInMinutes the number of minutes at which the trigger should repeat.
+     * @return the updated CalendarIntervalScheduleBuilder
+     * @see DailyTimeIntervalTrigger#getRepeatInterval()
+     * @see DailyTimeIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public DailyTimeIntervalScheduleBuilder withIntervalInMinutes(int intervalInMinutes) {
+        withInterval(intervalInMinutes, IntervalUnit.MINUTE);
+        return this;
+    }
+
+    /**
+     * Specify an interval in the IntervalUnit.HOUR that the produced 
+     * Trigger will repeat at.
+     * 
+     * @param intervalInHours the number of hours at which the trigger should repeat.
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     * @see DailyTimeIntervalTrigger#getRepeatInterval()
+     * @see DailyTimeIntervalTrigger#getRepeatIntervalUnit()
+     */
+    public DailyTimeIntervalScheduleBuilder withIntervalInHours(int intervalInHours) {
+        withInterval(intervalInHours, IntervalUnit.HOUR);
+        return this;
+    }
+
+    /**
+     * Set the trigger to fire on the given days of the week.
+     * 
+     * @param onDaysOfWeek a Set containing the integers representing the days of the week, per the values 1-7 as defined by 
+     * {@link java.util.Calendar#SUNDAY} - {@link java.util.Calendar#SATURDAY}. 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     */
+    public DailyTimeIntervalScheduleBuilder onDaysOfTheWeek(Set onDaysOfWeek) {
+        if(onDaysOfWeek == null || onDaysOfWeek.size() == 0)
+            throw new IllegalArgumentException("Days of week must be an non-empty set.");
+        for (Integer day : onDaysOfWeek)
+            if (!ALL_DAYS_OF_THE_WEEK.contains(day))
+                throw new IllegalArgumentException("Invalid value for day of week: " + day);
+                
+        this.daysOfWeek = onDaysOfWeek;
+        return this;
+    }
+
+    /**
+     * Set the trigger to fire on the given days of the week.
+     * 
+     * @param onDaysOfWeek a variable length list of Integers representing the days of the week, per the values 1-7 as 
+     * defined by {@link java.util.Calendar#SUNDAY} - {@link java.util.Calendar#SATURDAY}. 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     */
+    public DailyTimeIntervalScheduleBuilder onDaysOfTheWeek(Integer ... onDaysOfWeek) {
+        Set daysAsSet = new HashSet(12);
+        Collections.addAll(daysAsSet, onDaysOfWeek);
+        return onDaysOfTheWeek(daysAsSet);
+    }
+    
+    /**
+     * Set the trigger to fire on the days from Monday through Friday.
+     * 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     */
+    public DailyTimeIntervalScheduleBuilder onMondayThroughFriday() {
+        this.daysOfWeek = MONDAY_THROUGH_FRIDAY;
+        return this;
+    }
+
+    /**
+     * Set the trigger to fire on the days Saturday and Sunday.
+     * 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     */
+    public DailyTimeIntervalScheduleBuilder onSaturdayAndSunday() {
+        this.daysOfWeek = SATURDAY_AND_SUNDAY;
+        return this;
+    }
+
+    /**
+     * Set the trigger to fire on all days of the week.
+     * 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     */
+    public DailyTimeIntervalScheduleBuilder onEveryDay() {
+        this.daysOfWeek = ALL_DAYS_OF_THE_WEEK;
+        return this;
+    }
+
+    /**
+     * Set the trigger to begin firing each day at the given time.
+     * 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     */
+    public DailyTimeIntervalScheduleBuilder startingDailyAt(TimeOfDay timeOfDay) {
+        if(timeOfDay == null)
+            throw new IllegalArgumentException("Start time of day cannot be null!");
+        
+        this.startTimeOfDay = timeOfDay;
+        return this;
+    }
+
+    /**
+     * Set the startTimeOfDay for this trigger to end firing each day at the given time.
+     * 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     */
+    public DailyTimeIntervalScheduleBuilder endingDailyAt(TimeOfDay timeOfDay) {        
+        this.endTimeOfDay = timeOfDay;
+        return this;
+    }
+
+    /**
+     * Calculate and set the endTimeOfDay using count, interval and starTimeOfDay. This means
+     * that these must be set before this method is call.
+     * 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     */
+    public DailyTimeIntervalScheduleBuilder endingDailyAfterCount(int count) {
+        if(count <=0)
+            throw new IllegalArgumentException("Ending daily after count must be a positive number!");
+        
+        if(startTimeOfDay == null)
+            throw new IllegalArgumentException("You must set the startDailyAt() before calling this endingDailyAfterCount()!");
+        
+        Date today = new Date();
+        Date startTimeOfDayDate = startTimeOfDay.getTimeOfDayForDate(today);
+        Date maxEndTimeOfDayDate = TimeOfDay.hourMinuteAndSecondOfDay(23, 59, 59).getTimeOfDayForDate(today);
+        long remainingMillisInDay = maxEndTimeOfDayDate.getTime() - startTimeOfDayDate.getTime();
+        long intervalInMillis;
+        if (intervalUnit == IntervalUnit.SECOND)
+            intervalInMillis = interval * 1000L;
+        else if (intervalUnit == IntervalUnit.MINUTE)
+                intervalInMillis = interval * 1000L * 60;
+        else if (intervalUnit == IntervalUnit.HOUR)
+            intervalInMillis = interval * 1000L * 60 * 24;
+        else
+            throw new IllegalArgumentException("The IntervalUnit: " + intervalUnit + " is invalid for this trigger."); 
+        
+        if (remainingMillisInDay - intervalInMillis <= 0)
+            throw new IllegalArgumentException("The startTimeOfDay is too late with given Interval and IntervalUnit values.");
+        
+        long maxNumOfCount = (remainingMillisInDay / intervalInMillis);
+        if (count > maxNumOfCount)
+            throw new IllegalArgumentException("The given count " + count + " is too large! The max you can set is " + maxNumOfCount);
+        
+        long incrementInMillis = (count - 1) * intervalInMillis;
+        Date endTimeOfDayDate = new Date(startTimeOfDayDate.getTime() + incrementInMillis);
+        
+        if (endTimeOfDayDate.getTime() > maxEndTimeOfDayDate.getTime())
+            throw new IllegalArgumentException("The given count " + count + " is too large! The max you can set is " + maxNumOfCount);
+        
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(endTimeOfDayDate);
+        int hour = cal.get(Calendar.HOUR_OF_DAY);
+        int minute = cal.get(Calendar.MINUTE);
+        int second = cal.get(Calendar.SECOND);
+        
+        endTimeOfDay = TimeOfDay.hourMinuteAndSecondOfDay(hour, minute, second);
+        return this;
+    }
+
+    /**
+     * If the Trigger misfires, use the 
+     * {@link Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY} instruction.
+     * 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     * @see Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
+     */
+    public DailyTimeIntervalScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
+        misfireInstruction = Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY;
+        return this;
+    }
+    
+    /**
+     * If the Trigger misfires, use the 
+     * {@link DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_DO_NOTHING} instruction.
+     * 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     * @see DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_DO_NOTHING
+     */
+    public DailyTimeIntervalScheduleBuilder withMisfireHandlingInstructionDoNothing() {
+        misfireInstruction = DailyTimeIntervalTrigger.MISFIRE_INSTRUCTION_DO_NOTHING;
+        return this;
+    }
+
+    /**
+     * If the Trigger misfires, use the 
+     * {@link DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW} instruction.
+     * 
+     * @return the updated DailyTimeIntervalScheduleBuilder
+     * @see DailyTimeIntervalTrigger#MISFIRE_INSTRUCTION_FIRE_ONCE_NOW
+     */
+    public DailyTimeIntervalScheduleBuilder withMisfireHandlingInstructionFireAndProceed() {
+        misfireInstruction = CalendarIntervalTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW;
+        return this;
+    }
+    
+    /**
+     * Set number of times for interval to repeat.
+     * 
+     * 

Note: if you want total count = 1 (at start time) + repeatCount

+ * + * @return the new DailyTimeIntervalScheduleBuilder + */ + public DailyTimeIntervalScheduleBuilder withRepeatCount(int repeatCount) { + this.repeatCount = repeatCount; + return this; + } + + private void validateInterval(int timeInterval) { + if(timeInterval <= 0) + throw new IllegalArgumentException("Interval must be a positive value."); + } +} Index: 3rdParty_sources/quartz/org/quartz/DailyTimeIntervalTrigger.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/DailyTimeIntervalTrigger.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/DailyTimeIntervalTrigger.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,138 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.util.Calendar; +import java.util.Set; + +import org.quartz.DateBuilder.IntervalUnit; + +/** + * A {@link Trigger} that is used to fire a {@link org.quartz.JobDetail} + * based upon daily repeating time intervals. + * + *

The trigger will fire every N (see {@link #getRepeatInterval()} ) seconds, minutes or hours + * (see {@link #getRepeatIntervalUnit()}) during a given time window on specified days of the week.

+ * + *

For example#1, a trigger can be set to fire every 72 minutes between 8:00 and 11:00 everyday. It's fire times would + * be 8:00, 9:12, 10:24, then next day would repeat: 8:00, 9:12, 10:24 again.

+ * + *

For example#2, a trigger can be set to fire every 23 minutes between 9:20 and 16:47 Monday through Friday.

+ * + *

On each day, the starting fire time is reset to startTimeOfDay value, and then it will add repeatInterval value to it until + * the endTimeOfDay is reached. If you set daysOfWeek values, then fire time will only occur during those week days period.

+ * + *

The default values for fields if not set are: startTimeOfDay defaults to 00:00:00, the endTimeOfDay default to 23:59:59, + * and daysOfWeek is default to every day. The startTime default to current time-stamp now, while endTime has not value.

+ * + *

If startTime is before startTimeOfDay, then it has no affect. Else if startTime after startTimeOfDay, then the first fire time + * for that day will be normal startTimeOfDay incremental values after startTime value. Same reversal logic is applied to endTime + * with endTimeOfDay.

+ * + * @see DailyTimeIntervalScheduleBuilder + * + * @since 2.1.0 + * + * @author James House + * @author Zemian Deng + */ +public interface DailyTimeIntervalTrigger extends Trigger { + + /** + *

+ * 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; + + /** + *

+ * Instructs the {@link Scheduler} that upon a mis-fire + * situation, the {@link DailyTimeIntervalTrigger} wants to be + * fired now by Scheduler. + *

+ */ + public static final int MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = 1; + + /** + *

+ * Instructs the {@link Scheduler} that upon a mis-fire + * situation, the {@link DailyTimeIntervalTrigger} 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. + *

+ */ + public static final int MISFIRE_INSTRUCTION_DO_NOTHING = 2; + + /** + *

Get the interval unit - the time unit on with the interval applies.

+ * + *

The only intervals that are valid for this type of trigger are {@link IntervalUnit#SECOND}, + * {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}.

+ */ + public IntervalUnit getRepeatIntervalUnit(); + + /** + *

+ * Get the the number of times for interval this trigger should + * repeat, after which it will be automatically deleted. + *

+ * + * @see #REPEAT_INDEFINITELY + */ + public int getRepeatCount(); + + /** + *

+ * Get the the time interval that will be added to the DateIntervalTrigger's + * fire time (in the set repeat interval unit) in order to calculate the time of the + * next trigger repeat. + *

+ */ + public int getRepeatInterval(); + + /** + * The time of day to start firing at the given interval. + */ + public TimeOfDay getStartTimeOfDay(); + + /** + * The time of day to complete firing at the given interval. + */ + public TimeOfDay getEndTimeOfDay(); + + /** + * The days of the week upon which to fire. + * + * @return a Set containing the integers representing the days of the week, per the values 1-7 as defined by + * {@link java.util.Calendar#SUNDAY} - {@link java.util.Calendar#SATURDAY}. + */ + public Set getDaysOfWeek(); + + /** + *

+ * Get the number of times the DateIntervalTrigger has already + * fired. + *

+ */ + public int getTimesTriggered(); + + public TriggerBuilder getTriggerBuilder(); +} Index: 3rdParty_sources/quartz/org/quartz/DateBuilder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/DateBuilder.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/DateBuilder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,1021 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +/** + * DateBuilder is used to conveniently create + * java.util.Date instances that meet particular criteria. + * + *

Quartz provides a builder-style API for constructing scheduling-related + * entities via a Domain-Specific Language (DSL). The DSL can best be + * utilized through the usage of static imports of the methods on the classes + * TriggerBuilder, JobBuilder, + * DateBuilder, JobKey, TriggerKey + * and the various ScheduleBuilder implementations.

+ * + *

Client code can then use the DSL to write code such as this:

+ *
+ *         JobDetail job = newJob(MyJob.class)
+ *             .withIdentity("myJob")
+ *             .build();
+ *             
+ *         Trigger trigger = newTrigger() 
+ *             .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+ *             .withSchedule(simpleSchedule()
+ *                 .withIntervalInHours(1)
+ *                 .repeatForever())
+ *             .startAt(futureDate(10, MINUTES))
+ *             .build();
+ *         
+ *         scheduler.scheduleJob(job, trigger);
+ * 
+ *  
+ * @see TriggerBuilder
+ * @see JobBuilder 
+ */
+public class DateBuilder {
+
+    public enum IntervalUnit { MILLISECOND, SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, YEAR }
+    
+    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 JANUARY = 1;
+    
+    public static final int FEBRUARY = 2;
+
+    public static final int MARCH = 3;
+
+    public static final int APRIL = 4;
+
+    public static final int MAY = 5;
+
+    public static final int JUNE = 6;
+
+    public static final int JULY = 7;
+
+    public static final int AUGUST = 8;
+
+    public static final int SEPTEMBER = 9;
+
+    public static final int OCTOBER = 10;
+
+    public static final int NOVEMBER = 11;
+
+    public static final int DECEMBER = 12;
+
+    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_MOST_DAYS = 24l * 60l * 60L;
+
+    public static final long MILLISECONDS_IN_DAY = SECONDS_IN_MOST_DAYS * 1000l;
+    
+    private int month;
+    private int day;
+    private int year;
+    private int hour;
+    private int minute;
+    private int second;
+    private TimeZone tz;
+    private Locale lc;
+    
+    /**
+     * Create a DateBuilder, with initial settings for the current date and time in the system default timezone.
+     */
+    private DateBuilder() {
+        Calendar cal = Calendar.getInstance();
+        
+        month = cal.get(Calendar.MONTH) + 1;
+        day = cal.get(Calendar.DAY_OF_MONTH);
+        year = cal.get(Calendar.YEAR);
+        hour = cal.get(Calendar.HOUR_OF_DAY);
+        minute = cal.get(Calendar.MINUTE);
+        second = cal.get(Calendar.SECOND);
+    }
+
+    /**
+     * Create a DateBuilder, with initial settings for the current date and time in the given timezone.
+     */
+    private DateBuilder(TimeZone tz) {
+        Calendar cal = Calendar.getInstance(tz);
+        
+        this.tz = tz;
+        month = cal.get(Calendar.MONTH) + 1;
+        day = cal.get(Calendar.DAY_OF_MONTH);
+        year = cal.get(Calendar.YEAR);
+        hour = cal.get(Calendar.HOUR_OF_DAY);
+        minute = cal.get(Calendar.MINUTE);
+        second = cal.get(Calendar.SECOND);
+    }
+
+    /**
+     * Create a DateBuilder, with initial settings for the current date and time in the given locale.
+     */
+    private DateBuilder(Locale lc) {
+        Calendar cal = Calendar.getInstance(lc);
+        
+        this.lc = lc;
+        month = cal.get(Calendar.MONTH) + 1;
+        day = cal.get(Calendar.DAY_OF_MONTH);
+        year = cal.get(Calendar.YEAR);
+        hour = cal.get(Calendar.HOUR_OF_DAY);
+        minute = cal.get(Calendar.MINUTE);
+        second = cal.get(Calendar.SECOND);
+    }
+
+    /**
+     * Create a DateBuilder, with initial settings for the current date and time in the given timezone and locale.
+     */
+    private DateBuilder(TimeZone tz, Locale lc) {
+        Calendar cal = Calendar.getInstance(tz, lc);
+        
+        this.tz = tz;
+        this.lc = lc;
+        month = cal.get(Calendar.MONTH) + 1;
+        day = cal.get(Calendar.DAY_OF_MONTH);
+        year = cal.get(Calendar.YEAR);
+        hour = cal.get(Calendar.HOUR_OF_DAY);
+        minute = cal.get(Calendar.MINUTE);
+        second = cal.get(Calendar.SECOND);
+    }
+
+    /**
+     * Create a DateBuilder, with initial settings for the current date and time in the system default timezone.
+     */
+    public static DateBuilder newDate() {
+        return new DateBuilder();
+    }
+
+    /**
+     * Create a DateBuilder, with initial settings for the current date and time in the given timezone.
+     */
+    public static DateBuilder newDateInTimezone(TimeZone tz) {
+        return new DateBuilder(tz);
+    }
+
+    /**
+     * Create a DateBuilder, with initial settings for the current date and time in the given locale.
+     */
+    public static DateBuilder newDateInLocale(Locale lc) {
+        return new DateBuilder(lc);
+    }
+
+    /**
+     * Create a DateBuilder, with initial settings for the current date and time in the given timezone and locale.
+     */
+    public static DateBuilder newDateInTimeZoneAndLocale(TimeZone tz, Locale lc) {
+        return new DateBuilder(tz, lc);
+    }
+
+    /**
+     * Build the Date defined by this builder instance. 
+     */
+    public Date build() {
+        Calendar cal;
+
+        if(tz != null && lc != null)
+            cal = Calendar.getInstance(tz, lc);
+        else if(tz != null)
+            cal = Calendar.getInstance(tz);
+        else if(lc != null)
+            cal = Calendar.getInstance(lc);
+        else 
+          cal = Calendar.getInstance();
+        
+        cal.set(Calendar.YEAR, year);
+        cal.set(Calendar.MONTH, month - 1);
+        cal.set(Calendar.DAY_OF_MONTH, day);
+        cal.set(Calendar.HOUR_OF_DAY, hour);
+        cal.set(Calendar.MINUTE, minute);
+        cal.set(Calendar.SECOND, second);
+        cal.set(Calendar.MILLISECOND, 0);
+        
+        return cal.getTime();
+    }
+    
+    /**
+     * Set the hour (0-23) for the Date that will be built by this builder.
+     */
+    public DateBuilder atHourOfDay(int atHour) {
+        validateHour(atHour);
+        
+        this.hour = atHour;
+        return this;
+    }
+
+    /**
+     * Set the minute (0-59) for the Date that will be built by this builder.
+     */
+    public DateBuilder atMinute(int atMinute) {
+        validateMinute(atMinute);
+        
+        this.minute = atMinute;
+        return this;
+    }
+
+    /**
+     * Set the second (0-59) for the Date that will be built by this builder, and truncate the milliseconds to 000.
+     */
+    public DateBuilder atSecond(int atSecond) {
+        validateSecond(atSecond);
+        
+        this.second = atSecond;
+        return this;
+    }
+
+    public DateBuilder atHourMinuteAndSecond(int atHour, int atMinute, int atSecond) {
+        validateHour(atHour);
+        validateMinute(atMinute);
+        validateSecond(atSecond);
+        
+        this.hour = atHour;
+        this.second = atSecond;
+        this.minute = atMinute;
+        return this;
+    }
+    
+    /**
+     * Set the day of month (1-31) for the Date that will be built by this builder.
+     */
+    public DateBuilder onDay(int onDay) {
+        validateDayOfMonth(onDay);
+        
+        this.day = onDay;
+        return this;
+    }
+
+    /**
+     * Set the month (1-12) for the Date that will be built by this builder.
+     */
+    public DateBuilder inMonth(int inMonth) {
+        validateMonth(inMonth);
+        
+        this.month = inMonth;
+        return this;
+    }
+    
+    public DateBuilder inMonthOnDay(int inMonth, int onDay) {
+        validateMonth(inMonth);
+        validateDayOfMonth(onDay);
+        
+        this.month = inMonth;
+        this.day = onDay;
+        return this;
+    }
+
+    /**
+     * Set the year for the Date that will be built by this builder.
+     */
+    public DateBuilder inYear(int inYear) {
+        validateYear(inYear);
+        
+        this.year = inYear;
+        return this;
+    }
+
+    /**
+     * Set the TimeZone for the Date that will be built by this builder (if "null", system default will be used)
+     */
+    public DateBuilder inTimeZone(TimeZone timezone) {
+        this.tz = timezone;
+        return this;
+    }
+
+    /**
+     * Set the Locale for the Date that will be built by this builder (if "null", system default will be used)
+     */
+    public DateBuilder inLocale(Locale locale) {
+        this.lc = locale;
+        return this;
+    }
+
+    public static Date futureDate(int interval, IntervalUnit unit) {
+        
+        Calendar c = Calendar.getInstance();
+        c.setTime(new Date());
+        c.setLenient(true);
+        
+        c.add(translate(unit), interval);
+
+        return c.getTime();
+    }
+    
+
+    private static int translate(IntervalUnit unit) {
+        switch(unit) {
+            case DAY : return Calendar.DAY_OF_YEAR;
+            case HOUR : return Calendar.HOUR_OF_DAY;
+            case MINUTE : return Calendar.MINUTE;
+            case MONTH : return Calendar.MONTH;
+            case SECOND : return Calendar.SECOND;
+            case MILLISECOND : return Calendar.MILLISECOND;
+            case WEEK : return Calendar.WEEK_OF_YEAR;
+            case YEAR : return Calendar.YEAR;
+            default : throw new IllegalArgumentException("Unknown IntervalUnit");
+        }
+    }
+
+    /**
+     * 

+ * Get a Date object that represents the given time, on + * tomorrow'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 + * @return the new date + */ + public static Date tomorrowAt(int hour, int minute, int second) { + validateSecond(second); + validateMinute(minute); + validateHour(hour); + + Date date = new Date(); + + Calendar c = Calendar.getInstance(); + c.setTime(date); + c.setLenient(true); + + // advance one day + c.add(Calendar.DAY_OF_YEAR, 1); + + 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 + * today's date (equivalent to {@link #dateOf(int, int, int)}). + *

+ * + * @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 + * @return the new date + */ + public static Date todayAt(int hour, int minute, int second) { + return dateOf(hour, minute, second); + } + + /** + *

+ * Get a Date object that represents the given time, on + * today's date (equivalent to {@link #todayAt(int, int, int)}). + *

+ * + * @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 + * @return the new date + */ + public static Date dateOf(int hour, int minute, int second) { + 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 + * @return the new date + */ + public static Date dateOf(int hour, int minute, int second, + 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 + * @return the new date + */ + public static Date dateOf(int hour, int minute, int second, + 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 date that is rounded to the next even hour after the current time. + *

+ * + *

+ * For example a current 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. + *

+ * + * @return the new rounded date + */ + public static Date evenHourDateAfterNow() { + return evenHourDate(null); + } + /** + *

+ * 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 + * @return the new rounded date + */ + public static Date evenHourDate(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 + * @return the new rounded date + */ + public static Date evenHourDateBefore(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 after the current time. + *

+ * + *

+ * For example a current 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. + *

+ * + * @return the new rounded date + */ + public static Date evenMinuteDateAfterNow() { + return evenMinuteDate(null); + } + + /** + *

+ * 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, if null the current time will + * be used + * @return the new rounded date + */ + public static Date evenMinuteDate(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, if null the current time will + * be used + * @return the new rounded date + */ + public static Date evenMinuteDateBefore(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 after the current time. + *

+ * + * @return the new rounded date + */ + public static Date evenSecondDateAfterNow() { + return evenSecondDate(null); + } + /** + *

+ * 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 + * @return the new rounded date + */ + public static Date evenSecondDate(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:54.000. + *

+ * + * @param date + * the Date to round, if null the current time will + * be used + * @return the new rounded date + */ + public static Date evenSecondDateBefore(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 TimeMinute-BaseResult Time
11:16:412011:20:00
11:36:412011:40:00
11:46:412012:00:00
11:26:413011:30:00
11:36:413012:00:00
11:16:411711:17:00
11:17:411711:34:00
11:52:411712:00:00
11:52:41511:55:00
11:57:41512:00:00
11:17:41012:00:00
11:17:41111: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 + * @return the new rounded date + * + * @see #nextGivenSecondDate(Date, int) + */ + public static Date nextGivenMinuteDate(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 + * @return the new rounded date + * + * @see #nextGivenMinuteDate(Date, int) + */ + public static Date nextGivenSecondDate(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(); + } + } + + /** + * Translate a date & time from a users time zone to the another + * (probably server) time zone 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 = (dest.getOffset(date.getTime()) - src.getOffset(date.getTime())); + + newDate.setTime(date.getTime() - offset); + + return newDate; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////// + + public static void validateDayOfWeek(int dayOfWeek) { + if (dayOfWeek < SUNDAY || dayOfWeek > SATURDAY) { + throw new IllegalArgumentException("Invalid day of week."); + } + } + + public static void validateHour(int hour) { + if (hour < 0 || hour > 23) { + throw new IllegalArgumentException( + "Invalid hour (must be >= 0 and <= 23)."); + } + } + + public static void validateMinute(int minute) { + if (minute < 0 || minute > 59) { + throw new IllegalArgumentException( + "Invalid minute (must be >= 0 and <= 59)."); + } + } + + public static void validateSecond(int second) { + if (second < 0 || second > 59) { + throw new IllegalArgumentException( + "Invalid second (must be >= 0 and <= 59)."); + } + } + + public static void validateDayOfMonth(int day) { + if (day < 1 || day > 31) { + throw new IllegalArgumentException("Invalid day of month."); + } + } + + public static void validateMonth(int month) { + if (month < 1 || month > 12) { + throw new IllegalArgumentException( + "Invalid month (must be >= 1 and <= 12."); + } + } + + private static final int MAX_YEAR = Calendar.getInstance().get(Calendar.YEAR) + 100; + public static void validateYear(int year) { + if (year < 0 || year > MAX_YEAR) { + throw new IllegalArgumentException( + "Invalid year (must be >= 0 and <= " + MAX_YEAR); + } + } + +} Index: 3rdParty_sources/quartz/org/quartz/DisallowConcurrentExecution.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/DisallowConcurrentExecution.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/DisallowConcurrentExecution.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,40 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An annotation that marks a {@link Job} class as one that must not have multiple + * instances executed concurrently (where instance is based-upon a {@link JobDetail} + * definition - or in other words based upon a {@link JobKey}). + * + * @see PersistJobDataAfterExecution + * + * @author jhouse + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DisallowConcurrentExecution { + +} Index: 3rdParty_sources/quartz/org/quartz/ExecuteInJTATransaction.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/ExecuteInJTATransaction.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/ExecuteInJTATransaction.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,62 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.transaction.UserTransaction; + +/** + * An annotation that marks a {@link Job} class as one that will have its + * execution wrapped by a JTA Transaction. + * + *

If this annotation is present, Quartz will begin a JTA transaction + * before calling the execute() method, and will commit + * the transaction if the method does not throw an exception and the + * transaction has not had setRollbackOnly() called on it + * (otherwise the transaction will be rolled-back by Quartz).

+ * + *

This is essentially the same behavior as setting the configuration + * property org.quartz.scheduler.wrapJobExecutionInUserTransaction + * to true - except that it only affects the job that has + * the annotation, rather than all jobs (as the property does). If the + * property is set to true and the annotation is also set, + * then of course the annotation becomes redundant.

+ * + * @author jhouse + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ExecuteInJTATransaction { + + /** + * The JTA transaction timeout. + *

+ * If set then the {@code UserTransaction} timeout will be set to this + * value before beginning the transaction. + * + * @see UserTransaction#setTransactionTimeout(int) + * @return the transaction timeout. + */ + int timeout() default -1; +} Index: 3rdParty_sources/quartz/org/quartz/InterruptableJob.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/InterruptableJob.java (.../InterruptableJob.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/InterruptableJob.java (.../InterruptableJob.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,19 +16,17 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package 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 + * mechanism for having their execution interrupted. It is NOT a requirement * for jobs to implement this interface - in fact, for most people, none of * their jobs will. - *

* + *

Interrupting a Job is very analogous in concept and + * challenge to normal interruption of a Thread in Java. + * *

* The means of actually interrupting the Job must be implemented within the * Job itself (the interrupt() method of this @@ -51,18 +49,22 @@ * 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 + * Implementation 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 + * reference to the Thread when the execute(..) method exits (preferably in a * finally block. *

* + *

+ * See Example 7 (org.quartz.examples.example7.DumbInterruptableJob) for a simple + * implementation demonstration. + *

* @see Job * @see StatefulJob - * @see Scheduler#interrupt(String, String) - * @see org.quartz.examples.example7.DumbInterruptableJob + * @see Scheduler#interrupt(JobKey) + * @see Scheduler#interrupt(String) * * @author James House */ @@ -82,12 +84,9 @@ * interrupts the Job. *

* - * @return void (nothing) if job interrupt is successful. * @throws UnableToInterruptJobException * if there is an exception while interrupting the job. */ - public void interrupt() - throws UnableToInterruptJobException; - - + void interrupt() + throws UnableToInterruptJobException; } Index: 3rdParty_sources/quartz/org/quartz/Job.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/Job.java (.../Job.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/Job.java (.../Job.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,9 +16,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; /** @@ -38,7 +35,10 @@ *

* * @see JobDetail - * @see StatefulJob + * @see JobBuilder + * @see ExecuteInJTATransaction + * @see DisallowConcurrentExecution + * @see PersistJobDataAfterExecution * @see Trigger * @see Scheduler * @@ -70,11 +70,10 @@ * execution. *

* - * @return void (nothing) if job is successful. * @throws JobExecutionException * if there is an exception while executing the job. */ - public void execute(JobExecutionContext context) - throws JobExecutionException; + void execute(JobExecutionContext context) + throws JobExecutionException; } Index: 3rdParty_sources/quartz/org/quartz/JobBuilder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/JobBuilder.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/JobBuilder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,351 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import org.quartz.impl.JobDetailImpl; +import org.quartz.utils.Key; + +/** + * JobBuilder is used to instantiate {@link JobDetail}s. + * + *

The builder will always try to keep itself in a valid state, with + * reasonable defaults set for calling build() at any point. For instance + * if you do not invoke withIdentity(..) a job name will be generated + * for you.

+ * + *

Quartz provides a builder-style API for constructing scheduling-related + * entities via a Domain-Specific Language (DSL). The DSL can best be + * utilized through the usage of static imports of the methods on the classes + * TriggerBuilder, JobBuilder, + * DateBuilder, JobKey, TriggerKey + * and the various ScheduleBuilder implementations.

+ * + *

Client code can then use the DSL to write code such as this:

+ *
+ *         JobDetail job = newJob(MyJob.class)
+ *             .withIdentity("myJob")
+ *             .build();
+ *             
+ *         Trigger trigger = newTrigger() 
+ *             .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+ *             .withSchedule(simpleSchedule()
+ *                 .withIntervalInHours(1)
+ *                 .repeatForever())
+ *             .startAt(futureDate(10, MINUTES))
+ *             .build();
+ *         
+ *         scheduler.scheduleJob(job, trigger);
+ * 
+ *  
+ * @see TriggerBuilder
+ * @see DateBuilder 
+ * @see JobDetail
+ */
+public class JobBuilder {
+
+    private JobKey key;
+    private String description;
+    private Class jobClass;
+    private boolean durability;
+    private boolean shouldRecover;
+    
+    private JobDataMap jobDataMap = new JobDataMap();
+    
+    protected JobBuilder() {
+    }
+    
+    /**
+     * Create a JobBuilder with which to define a JobDetail.
+     * 
+     * @return a new JobBuilder
+     */
+    public static JobBuilder newJob() {
+        return new JobBuilder();
+    }
+    
+    /**
+     * Create a JobBuilder with which to define a JobDetail,
+     * and set the class name of the Job to be executed.
+     * 
+     * @return a new JobBuilder
+     */
+    public static JobBuilder newJob(Class  jobClass) {
+        JobBuilder b = new JobBuilder();
+        b.ofType(jobClass);
+        return b;
+    }
+
+    /**
+     * Produce the JobDetail instance defined by this 
+     * JobBuilder.
+     * 
+     * @return the defined JobDetail.
+     */
+    public JobDetail build() {
+
+        JobDetailImpl job = new JobDetailImpl();
+        
+        job.setJobClass(jobClass);
+        job.setDescription(description);
+        if(key == null)
+            key = new JobKey(Key.createUniqueName(null), null);
+        job.setKey(key); 
+        job.setDurability(durability);
+        job.setRequestsRecovery(shouldRecover);
+        
+        
+        if(!jobDataMap.isEmpty())
+            job.setJobDataMap(jobDataMap);
+        
+        return job;
+    }
+    
+    /**
+     * Use a JobKey with the given name and default group to
+     * identify the JobDetail.
+     * 
+     * 

If none of the 'withIdentity' methods are set on the JobBuilder, + * then a random, unique JobKey will be generated.

+ * + * @param name the name element for the Job's JobKey + * @return the updated JobBuilder + * @see JobKey + * @see JobDetail#getKey() + */ + public JobBuilder withIdentity(String name) { + key = new JobKey(name, null); + return this; + } + + /** + * Use a JobKey with the given name and group to + * identify the JobDetail. + * + *

If none of the 'withIdentity' methods are set on the JobBuilder, + * then a random, unique JobKey will be generated.

+ * + * @param name the name element for the Job's JobKey + * @param group the group element for the Job's JobKey + * @return the updated JobBuilder + * @see JobKey + * @see JobDetail#getKey() + */ + public JobBuilder withIdentity(String name, String group) { + key = new JobKey(name, group); + return this; + } + + /** + * Use a JobKey to identify the JobDetail. + * + *

If none of the 'withIdentity' methods are set on the JobBuilder, + * then a random, unique JobKey will be generated.

+ * + * @param jobKey the Job's JobKey + * @return the updated JobBuilder + * @see JobKey + * @see JobDetail#getKey() + */ + public JobBuilder withIdentity(JobKey jobKey) { + this.key = jobKey; + return this; + } + + /** + * Set the given (human-meaningful) description of the Job. + * + * @param jobDescription the description for the Job + * @return the updated JobBuilder + * @see JobDetail#getDescription() + */ + public JobBuilder withDescription(String jobDescription) { + this.description = jobDescription; + return this; + } + + /** + * Set the class which will be instantiated and executed when a + * Trigger fires that is associated with this JobDetail. + * + * @param jobClazz a class implementing the Job interface. + * @return the updated JobBuilder + * @see JobDetail#getJobClass() + */ + public JobBuilder ofType(Class jobClazz) { + this.jobClass = jobClazz; + return this; + } + + /** + * 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. + *

+ * + * @return the updated JobBuilder + * @see JobDetail#requestsRecovery() + */ + public JobBuilder requestRecovery() { + this.shouldRecover = true; + return this; + } + + /** + * 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. + *

+ * + * @param jobShouldRecover the desired setting + * @return the updated JobBuilder + */ + public JobBuilder requestRecovery(boolean jobShouldRecover) { + this.shouldRecover = jobShouldRecover; + return this; + } + + /** + * 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 + * - this method sets the value to true. + *

+ * + * @return the updated JobBuilder + * @see JobDetail#isDurable() + */ + public JobBuilder storeDurably() { + this.durability = true; + return this; + } + + /** + * 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. + *

+ * + * @param jobDurability the value to set for the durability property. + * @return the updated JobBuilder + * @see JobDetail#isDurable() + */ + public JobBuilder storeDurably(boolean jobDurability) { + this.durability = jobDurability; + return this; + } + + /** + * Add the given key-value pair to the JobDetail's {@link JobDataMap}. + * + * @return the updated JobBuilder + * @see JobDetail#getJobDataMap() + */ + public JobBuilder usingJobData(String dataKey, String value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the JobDetail's {@link JobDataMap}. + * + * @return the updated JobBuilder + * @see JobDetail#getJobDataMap() + */ + public JobBuilder usingJobData(String dataKey, Integer value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the JobDetail's {@link JobDataMap}. + * + * @return the updated JobBuilder + * @see JobDetail#getJobDataMap() + */ + public JobBuilder usingJobData(String dataKey, Long value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the JobDetail's {@link JobDataMap}. + * + * @return the updated JobBuilder + * @see JobDetail#getJobDataMap() + */ + public JobBuilder usingJobData(String dataKey, Float value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the JobDetail's {@link JobDataMap}. + * + * @return the updated JobBuilder + * @see JobDetail#getJobDataMap() + */ + public JobBuilder usingJobData(String dataKey, Double value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the JobDetail's {@link JobDataMap}. + * + * @return the updated JobBuilder + * @see JobDetail#getJobDataMap() + */ + public JobBuilder usingJobData(String dataKey, Boolean value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add all the data from the given {@link JobDataMap} to the + * {@code JobDetail}'s {@code JobDataMap}. + * + * @return the updated JobBuilder + * @see JobDetail#getJobDataMap() + */ + public JobBuilder usingJobData(JobDataMap newJobDataMap) { + jobDataMap.putAll(newJobDataMap); + return this; + } + + /** + * Replace the {@code JobDetail}'s {@link JobDataMap} with the + * given {@code JobDataMap}. + * + * @return the updated JobBuilder + * @see JobDetail#getJobDataMap() + */ + public JobBuilder setJobData(JobDataMap newJobDataMap) { + jobDataMap = newJobDataMap; + return this; + } +} Index: 3rdParty_sources/quartz/org/quartz/JobDataMap.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/JobDataMap.java (.../JobDataMap.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/JobDataMap.java (.../JobDataMap.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,26 +16,20 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; import java.io.Serializable; -import java.util.Iterator; import java.util.Map; -import org.quartz.utils.DirtyFlagMap; +import org.quartz.utils.StringKeyDirtyFlagMap; /** - *

* 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. + * jobs annotated with @PersistJobDataAfterExecution. *

* *

@@ -54,13 +48,13 @@ *

* * @see Job - * @see StatefulJob + * @see PersistJobDataAfterExecution * @see Trigger * @see JobExecutionContext * * @author James House */ -public class JobDataMap extends DirtyFlagMap implements Serializable { +public class JobDataMap extends StringKeyDirtyFlagMap implements Serializable { private static final long serialVersionUID = -6939901990106713909L; @@ -72,16 +66,6 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private boolean allowsTransientData = false; - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Constructors. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - /** *

* Create an empty JobDataMap. @@ -96,10 +80,11 @@ * Create a JobDataMap with the given data. *

*/ - public JobDataMap(Map map) { + public JobDataMap(Map map) { this(); - - putAll(map); + @SuppressWarnings("unchecked") // casting to keep API compatible and avoid compiler errors/warnings. + Map mapTyped = (Map)map; + putAll(mapTyped); } /* @@ -112,163 +97,12 @@ /** *

- * Tell the JobDataMap that it should allow non- Serializable - * data. - *

- * - *

- * If the JobDataMap does contain non- Serializable - * objects, and it belongs to a non-volatile Job that is - * stored in a JobStore that supports persistence, then - * those elements will be nulled-out during persistence. - *

- */ - 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; - } - - public boolean getAllowsTransientData() { - return allowsTransientData; - } - - 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; - } - - /** - *

- * Nulls-out any data values that are non-Serializable. - *

- */ - 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 JobDataMap. - *

- * - *

- * All keys must be Strings, and all values must be Serializable. - *

- */ - public void putAll(Map map) { - Iterator itr = map.keySet().iterator(); - while (itr.hasNext()) { - Object key = itr.next(); - Object val = map.get(key); - - put(key, val); - // will throw IllegalArgumentException if value not serilizable - } - } - - /** - *

- * Adds the given int value to the Job's - * data map. - *

- */ - public void put(String key, int value) { - super.put(key, new Integer(value)); - } - - /** - *

- * Adds the given long value to the Job's - * data map. - *

- */ - public void put(String key, long value) { - super.put(key, new Long(value)); - } - - /** - *

- * Adds the given float value to the Job's - * data map. - *

- */ - public void put(String key, float value) { - super.put(key, new Float(value)); - } - - /** - *

- * Adds the given double value to the Job's - * data map. - *

- */ - public void put(String key, double value) { - super.put(key, new Double(value)); - } - - /** - *

- * Adds the given boolean value to the Job's - * data map. - *

- */ - public void put(String key, boolean value) { - super.put(key, new Boolean(value)); - } - - /** - *

- * Adds the given char value to the Job's - * data map. - *

- */ - public void put(String key, char value) { - super.put(key, new Character(value)); - } - - /** - *

- * Adds the given String value to the Job's - * data map. - *

- */ - public void put(String key, String value) { - super.put(key, value); - } - - /** - *

* Adds the given boolean value as a string version to the * Job's data map. *

*/ public void putAsString(String key, boolean value) { - String strValue = new Boolean(value).toString(); + String strValue = Boolean.valueOf(value).toString(); super.put(key, strValue); } @@ -292,7 +126,7 @@ *

*/ public void putAsString(String key, char value) { - String strValue = new Character(value).toString(); + String strValue = Character.valueOf(value).toString(); super.put(key, strValue); } @@ -316,7 +150,7 @@ *

*/ public void putAsString(String key, double value) { - String strValue = new Double(value).toString(); + String strValue = Double.toString(value); super.put(key, strValue); } @@ -340,7 +174,7 @@ *

*/ public void putAsString(String key, float value) { - String strValue = new Float(value).toString(); + String strValue = Float.toString(value); super.put(key, strValue); } @@ -364,7 +198,7 @@ *

*/ public void putAsString(String key, int value) { - String strValue = new Integer(value).toString(); + String strValue = Integer.valueOf(value).toString(); super.put(key, strValue); } @@ -388,7 +222,7 @@ *

*/ public void putAsString(String key, long value) { - String strValue = new Long(value).toString(); + String strValue = Long.valueOf(value).toString(); super.put(key, strValue); } @@ -407,156 +241,16 @@ /** *

- * Adds the given Serializable object value to the JobDataMap. - *

- */ - 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 JobDataMap. *

* * @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 JobDataMap. - *

- * - * @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 JobDataMap. - *

- * - * @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 JobDataMap. - *

- * - * @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 JobDataMap. - *

- * - * @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 JobDataMap. - *

- * - * @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 JobDataMap. - *

- * - * @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."); - } - } - - /** - *

- * Retrieve the identified int value from the JobDataMap. - *

- * - * @throws ClassCastException - * if the identified object is not a String. - */ public int getIntFromString(String key) { Object obj = get(key); - return new Integer((String) obj).intValue(); + return new Integer((String) obj); } /** @@ -565,15 +259,16 @@ *

* * @throws ClassCastException - * if the identified object is not a String or Integeger. + * if the identified object is not a String or Integer. */ - public long getIntValue(String key) { + public int getIntValue(String key) { Object obj = get(key); - if(obj instanceof String) + if(obj instanceof String) { return getIntFromString(key); - else + } else { return getInt(key); + } } /** @@ -601,7 +296,7 @@ public boolean getBooleanValueFromString(String key) { Object obj = get(key); - return new Boolean((String) obj).booleanValue(); + return Boolean.valueOf((String) obj); } /** @@ -616,10 +311,11 @@ public boolean getBooleanValue(String key) { Object obj = get(key); - if(obj instanceof String) + if(obj instanceof String) { return getBooleanValueFromString(key); - else + } else { return getBoolean(key); + } } /** @@ -633,7 +329,7 @@ public Boolean getBooleanFromString(String key) { Object obj = get(key); - return new Boolean((String) obj); + return Boolean.valueOf((String) obj); } /** @@ -661,7 +357,7 @@ public Character getCharacterFromString(String key) { Object obj = get(key); - return new Character(((String) obj).charAt(0)); + return ((String) obj).charAt(0); } /** @@ -675,7 +371,7 @@ public double getDoubleValueFromString(String key) { Object obj = get(key); - return new Double((String) obj).doubleValue(); + return Double.valueOf((String) obj); } /** @@ -689,10 +385,11 @@ public double getDoubleValue(String key) { Object obj = get(key); - if(obj instanceof String) + if(obj instanceof String) { return getDoubleValueFromString(key); - else + } else { return getDouble(key); + } } /** @@ -720,7 +417,7 @@ public float getFloatValueFromString(String key) { Object obj = get(key); - return new Float((String) obj).floatValue(); + return new Float((String) obj); } /** @@ -734,10 +431,11 @@ public float getFloatValue(String key) { Object obj = get(key); - if(obj instanceof String) + if(obj instanceof String) { return getFloatValueFromString(key); - else + } else { return getFloat(key); + } } /** @@ -765,7 +463,7 @@ public long getLongValueFromString(String key) { Object obj = get(key); - return new Long((String) obj).longValue(); + return new Long((String) obj); } /** @@ -779,10 +477,11 @@ public long getLongValue(String key) { Object obj = get(key); - if(obj instanceof String) + if(obj instanceof String) { return getLongValueFromString(key); - else + } else { return getLong(key); + } } /** @@ -798,9 +497,4 @@ return new Long((String) obj); } - - public String[] getKeys() { - return (String[]) keySet().toArray(new String[size()]); - } - } Index: 3rdParty_sources/quartz/org/quartz/JobDetail.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/JobDetail.java (.../JobDetail.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/JobDetail.java (.../JobDetail.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,5 @@ - -/* - * Copyright 2004-2005 OpenSymphony +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. * * 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 @@ -16,364 +15,67 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; -import java.util.ArrayList; +import java.io.Serializable; /** - *

- * Conveys the detail properties of a given Job instance. - *

+ * Conveys the detail properties of a given Job instance. JobDetails are + * to be created/defined with {@link JobBuilder}. * *

* 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 + * Jobs 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, + * Triggers are the 'mechanism' by which Jobs + * are scheduled. Many Triggers can point to the same Job, * but a single Trigger can only point to one Job. *

* + * @see JobBuilder * @see Job - * @see StatefulJob * @see JobDataMap * @see Trigger * * @author James House - * @author Sharada Jambula */ -public class JobDetail implements Cloneable, java.io.Serializable { +public interface JobDetail extends Serializable, Cloneable { - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ + public JobKey getKey(); - private String name; - - private String group = Scheduler.DEFAULT_GROUP; - - private String description; - - private Class jobClass; - - private JobDataMap jobDataMap; - - private boolean volatility = false; - - private boolean durability = false; - - private boolean shouldRecover = false; - - private ArrayList jobListeners = new ArrayList(2); - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Constructors. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - /** *

- * 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. - *

- * - * @param group if 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. - *

- * - * @param group if 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. - *

- */ - public String getName() { - return name; - } - - /** - *

- * Set the name of this Job. - *

- * - * @exception IllegalArgumentException - * if name is null or empty. - */ - public void setName(String name) { - if (name == null || name.trim().length() == 0) - throw new IllegalArgumentException("Job name cannot be empty."); - - this.name = name; - } - - /** - *

- * Get the group of this Job. - *

- */ - public String getGroup() { - return group; - } - - /** - *

- * Set the group of this Job. - *

- * - * @param group if 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 Trigger in the format - * "group.name". - *

- */ - public String getFullName() { - return group + "." + name; - } - - /** - *

* Return the description given to the Job instance by its * creator (if any). *

* * @return null if no description was set. */ - public String getDescription() { - return description; - } + public String getDescription(); /** *

- * 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. - *

- */ - public void setDescription(String description) { - this.description = description; - } - - /** - *

* Get the instance of Job that will be executed. *

*/ - public Class getJobClass() { - return jobClass; - } + public Class getJobClass(); /** *

- * Set the instance of Job that will be executed. - *

- * - * @exception IllegalArgumentException - * if jobClass is null or the class is not a 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. *

*/ - public JobDataMap getJobDataMap() { - if (jobDataMap == null) jobDataMap = new JobDataMap(); - return jobDataMap; - } + public JobDataMap getJobDataMap(); /** *

- * Set the JobDataMap to be associated with the Job. - *

- */ - public void setJobDataMap(JobDataMap jobDataMap) { - this.jobDataMap = jobDataMap; - } - - /** - *

- * 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 org.quartz.spi.JobStore} for re-use after program - * restarts. - *

- * - *

- * If not explicitly set, the default value is false. - *

- */ - public void setVolatility(boolean volatility) { - this.volatility = volatility; - } - - /** - *

- * 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. - *

- */ - public void setDurability(boolean durability) { - this.durability = durability; - } - - /** - *

- * 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. - *

- * - * @see JobExecutionContext#isRecovering() - * @see JobExecutionContext#isFailedOver() - */ - public void setRequestsRecovery(boolean shouldRecover) { - this.shouldRecover = shouldRecover; - } - - /** - *

- * Whether or not the Job should not be persisted in the - * {@link org.quartz.spi.JobStore} for re-use after program - * restarts. - *

- * - *

- * If not explicitly set, the default value is false. - *

- * - * @return 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). *

@@ -385,21 +87,19 @@ * @return true if the Job should remain persisted after * being orphaned. */ - public boolean isDurable() { - return durability; - } + public boolean isDurable(); /** - *

- * Whether or not the Job implements the interface {@link StatefulJob}. - *

+ * @see PersistJobDataAfterExecution + * @return whether the associated Job class carries the {@link PersistJobDataAfterExecution} annotation. */ - public boolean isStateful() { - if (jobClass == null) - return false; + public boolean isPersistJobDataAfterExecution(); - return (StatefulJob.class.isAssignableFrom(jobClass)); - } + /** + * @see DisallowConcurrentExecution + * @return whether the associated Job class carries the {@link DisallowConcurrentExecution} annotation. + */ + public boolean isConcurrentExectionDisallowed(); /** *

@@ -413,70 +113,15 @@ *

* * @see JobExecutionContext#isRecovering() - * @see JobExecutionContext#isFailedOver() */ - public boolean requestsRecovery() { - return shouldRecover; - } + public boolean requestsRecovery(); + public Object clone(); + /** - *

- * Add the specified name of a {@link JobListener} to the - * end of the Job's list of listeners. - *

+ * Get a {@link JobBuilder} that is configured to produce a + * JobDetail identical to this one. */ - public void addJobListener(String name) { - jobListeners.add(name); - } + public JobBuilder getJobBuilder(); - /** - *

- * Remove the specified name of a {@link JobListener} from - * the Job's list of listeners. - *

- * - * @return true if the given name was found in the list, and removed - */ - public boolean removeJobListener(String name) { - return jobListeners.remove(name); - } - - /** - *

- * 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. - *

- */ - public String[] getJobListenerNames() { - return (String[]) jobListeners.toArray(new String[jobListeners.size()]); - } - - /** - *

- * 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 Object clone() { - JobDetail copy; - try { - copy = (JobDetail) super.clone(); - copy.jobListeners = (ArrayList) jobListeners.clone(); - if (jobDataMap != null) - copy.jobDataMap = (JobDataMap) jobDataMap.clone(); - } catch (CloneNotSupportedException ex) { - throw new IncompatibleClassChangeError("Not Cloneable."); - } - - return copy; - } - -} +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/JobExecutionContext.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/JobExecutionContext.java (.../JobExecutionContext.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/JobExecutionContext.java (.../JobExecutionContext.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,5 @@ - -/* - * Copyright 2004-2005 OpenSymphony +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. * * 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 @@ -16,23 +15,15 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; import java.util.Date; -import java.util.HashMap; -import org.quartz.spi.TriggerFiredBundle; - /** - *

* A context bundle containing handles to various environment information, that * is given to a {@link 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 @@ -43,13 +34,14 @@ * 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. + * job's own JobDataMap - even if it has the + * @PersistJobDataAfterExecution annotation. *

* *

* JobExecutionContext s are also returned from the * Scheduler.getCurrentlyExecutingJobs() - * method. These are the same instances as those past into the jobs that are + * 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 JobExecutionContexts, and their references to @@ -58,145 +50,66 @@ * to the job instance that is running). *

* - * @see #getJobDetail() * @see #getScheduler() * @see #getMergedJobDataMap() + * @see #getJobDetail() * * @see Job * @see Trigger * @see JobDataMap * * @author James House */ -public class JobExecutionContext implements java.io.Serializable { +public interface JobExecutionContext { - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - private transient Scheduler scheduler; - - private Trigger trigger; - - private JobDetail jobDetail; - - private JobDataMap jobDataMap; - - private transient Job job; - - private Calendar calendar; - - private boolean recovering = false; - - private int numRefires = 0; - - private Date fireTime; - - private Date scheduledFireTime; - - private Date prevFireTime; - - private Date nextFireTime; - - private long jobRunTime = -1; - - private Object result; - - private HashMap data = new HashMap(); - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Constructors. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - /** *

- * 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()); - - this.jobDataMap.setMutable(false); - this.trigger.getJobDataMap().setMutable(false); - } - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - *

* Get a handle to the Scheduler instance that fired the * Job. *

*/ - public Scheduler getScheduler() { - return scheduler; - } + public Scheduler getScheduler(); /** *

* Get a handle to the Trigger instance that fired the * Job. *

*/ - public Trigger getTrigger() { - return trigger; - } + public Trigger getTrigger(); /** *

* Get a handle to the Calendar referenced by the Trigger * instance that fired the Job. *

*/ - public Calendar getCalendar() { - return calendar; - } + public Calendar getCalendar(); /** *

* If the Job is being re-executed because of a 'recovery' * situation, this method will return true. *

*/ - public boolean isRecovering() { - return recovering; - } + public boolean isRecovering(); - public void incrementRefireCount() { - numRefires++; - } + /** + * Return the {@code TriggerKey} of the originally scheduled and now recovering job. + *

+ * When recovering a previously failed job execution this method returns the identity + * of the originally firing trigger. This recovering job will have been scheduled for + * the same firing time as the original job, and so is available via the + * {@link #getScheduledFireTime()} method. The original firing time of the job can be + * accessed via the {@link Scheduler#FAILED_JOB_ORIGINAL_TRIGGER_FIRETIME_IN_MILLISECONDS} + * element of this job's {@code JobDataMap}. + * + * @return the recovering trigger details + * @throws IllegalStateException if this is not a recovering job. + */ + public TriggerKey getRecoveringTriggerKey() throws IllegalStateException; - public int getRefireCount() { - return numRefires; - } + public int getRefireCount(); /** *

@@ -209,12 +122,12 @@ * 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 + * 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. + *

NOTE: Do not expect value 'set' into this JobDataMap to somehow be set + * or persisted back onto a job's own JobDataMap - even if it has the + * @PersistJobDataAfterExecution annotation. *

* *

@@ -223,18 +136,14 @@ *

* */ - public JobDataMap getMergedJobDataMap() { - return jobDataMap; - } + public JobDataMap getMergedJobDataMap(); /** *

* Get the JobDetail associated with the Job. *

*/ - public JobDetail getJobDetail() { - return jobDetail; - } + public JobDetail getJobDetail(); /** *

@@ -247,9 +156,7 @@ * interfaces. *

*/ - public Job getJobInstance() { - return job; - } + public Job getJobInstance(); /** * The actual time the trigger fired. For instance the scheduled time may @@ -259,9 +166,7 @@ * @return Returns the fireTime. * @see #getScheduledFireTime() */ - public Date getFireTime() { - return fireTime; - } + public Date getFireTime(); /** * The scheduled time the trigger fired for. For instance the scheduled @@ -271,29 +176,23 @@ * @return Returns the scheduledFireTime. * @see #getFireTime() */ - public Date getScheduledFireTime() { - return scheduledFireTime; - } + public Date getScheduledFireTime(); - public Date getPreviousFireTime() { - return prevFireTime; - } + public Date getPreviousFireTime(); - public Date getNextFireTime() { - return nextFireTime; - } + public Date getNextFireTime(); - public String toString() { - return "JobExecutionContext:" + " trigger: '" - + getTrigger().getFullName() + " job: " - + getJobDetail().getFullName() + " fireTime: '" + getFireTime() - + " scheduledFireTime: " + getScheduledFireTime() - + " previousFireTime: '" + getPreviousFireTime() - + " nextFireTime: " + getNextFireTime() + " isRecovering: " - + isRecovering() + " refireCount: " + getRefireCount(); - } - /** + * Get the unique Id that identifies this particular firing instance of the + * trigger that triggered this job execution. It is unique to this + * JobExecutionContext instance as well. + * + * @return the unique fire instance id + * @see Scheduler#interrupt(String) + */ + public String getFireInstanceId(); + + /** * Returns the result (if any) that the Job set before its * execution completed (the type of object set as the result is entirely up * to the particular job). @@ -307,10 +206,8 @@ * * @return Returns the result. */ - public Object getResult() { - return result; - } - + public Object getResult(); + /** * Set the result (if any) of the Job's execution (the type of * object set as the result is entirely up to the particular job). @@ -321,13 +218,9 @@ * {@link TriggerListener}s that are watching the job's * execution. *

- * - * @return Returns the result. */ - public void setResult(Object result) { - this.result = result; - } - + public void setResult(Object result); + /** * The amount of time the job ran for (in milliseconds). The returned * value will be -1 until the job has actually completed (or thrown an @@ -336,16 +229,7 @@ * * @return Returns the jobRunTime. */ - public long getJobRunTime() { - return jobRunTime; - } - - /** - * @param jobRunTime The jobRunTime to set. - */ - public void setJobRunTime(long jobRunTime) { - this.jobRunTime = jobRunTime; - } + public long getJobRunTime(); /** * Put the specified value into the context's data map with the given key. @@ -355,19 +239,16 @@ * completes, and all TriggerListeners and JobListeners have been * notified.

* - * @param key - * @param value + * @param key the key for the associated value + * @param value the value to store */ - public void put(Object key, Object value) { - data.put(key, value); - } - + public void put(Object key, Object value); + /** * Get the value with the given key from the context's data map. * - * @param key + * @param key the key for the desired value */ - public Object get(Object key) { - return data.get(key); - } -} + public Object get(Object key); + +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/JobExecutionException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/JobExecutionException.java (.../JobExecutionException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/JobExecutionException.java (.../JobExecutionException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,19 +16,14 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; /** - *

* An exception that can be thrown by a {@link org.quartz.Job} * to indicate to the Quartz {@link Scheduler} that an error - * occured while executing, and whether or not the Job requests + * occurred 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 @@ -43,6 +38,8 @@ */ public class JobExecutionException extends SchedulerException { + private static final long serialVersionUID = 1326342535829043325L; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -79,7 +76,7 @@ * Create a JobExcecutionException, with the given cause. *

*/ - public JobExecutionException(Exception cause) { + public JobExecutionException(Throwable cause) { super(cause); } @@ -108,7 +105,7 @@ * the 're-fire immediately' flag set to the given value. *

*/ - public JobExecutionException(Exception cause, boolean refireImmediately) { + public JobExecutionException(Throwable cause, boolean refireImmediately) { super(cause); refire = refireImmediately; @@ -117,16 +114,36 @@ /** *

* 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, Exception cause, + 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; + } + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -135,6 +152,10 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + public void setRefireImmediately(boolean refire) { + this.refire = refire; + } + public boolean refireImmediately() { return refire; } Index: 3rdParty_sources/quartz/org/quartz/JobKey.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/JobKey.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/JobKey.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,77 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import org.quartz.utils.Key; + +/** + * Uniquely identifies a {@link JobDetail}. + * + *

Keys are composed of both a name and group, and the name must be unique + * within the group. If only a group is specified then the default group + * name will be used.

+ * + *

Quartz provides a builder-style API for constructing scheduling-related + * entities via a Domain-Specific Language (DSL). The DSL can best be + * utilized through the usage of static imports of the methods on the classes + * TriggerBuilder, JobBuilder, + * DateBuilder, JobKey, TriggerKey + * and the various ScheduleBuilder implementations.

+ * + *

Client code can then use the DSL to write code such as this:

+ *
+ *         JobDetail job = newJob(MyJob.class)
+ *             .withIdentity("myJob")
+ *             .build();
+ *             
+ *         Trigger trigger = newTrigger() 
+ *             .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+ *             .withSchedule(simpleSchedule()
+ *                 .withIntervalInHours(1)
+ *                 .repeatForever())
+ *             .startAt(futureDate(10, MINUTES))
+ *             .build();
+ *         
+ *         scheduler.scheduleJob(job, trigger);
+ * 
+ *  
+ * 
+ * @see Job
+ * @see Key#DEFAULT_GROUP
+ */
+public final class JobKey extends Key {
+
+    private static final long serialVersionUID = -6073883950062574010L;
+    
+    public JobKey(String name) {
+        super(name, null);
+    }
+
+    public JobKey(String name, String group) {
+        super(name, group);
+    }
+
+    public static JobKey jobKey(String name) {
+        return new JobKey(name, null);
+    }
+    
+    public static JobKey jobKey(String name, String group) {
+        return new JobKey(name, group);
+    }
+
+}
Index: 3rdParty_sources/quartz/org/quartz/JobListener.java
===================================================================
diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b
--- 3rdParty_sources/quartz/org/quartz/JobListener.java	(.../JobListener.java)	(revision 2e3463e873227c6a3edcb3e02d55270219e553ff)
+++ 3rdParty_sources/quartz/org/quartz/JobListener.java	(.../JobListener.java)	(revision c208628989d52041b3765784f4c8cbfd6c80d47b)
@@ -1,6 +1,6 @@
 
 /* 
- * Copyright 2004-2005 OpenSymphony 
+ * Copyright 2001-2009 Terracotta, Inc. 
  * 
  * 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 
@@ -16,20 +16,16 @@
  * 
  */
 
-/*
- * Previously Copyright (c) 2001-2004 James House
- */
 package org.quartz;
 
 /**
- * 

* The interface to be implemented by classes that want to be informed when a * {@link org.quartz.JobDetail} executes. In general, * applications that use a Scheduler will not have use for this * mechanism. - *

* - * @see Scheduler + * @see ListenerManager#addJobListener(JobListener, Matcher) + * @see Matcher * @see Job * @see JobExecutionContext * @see JobExecutionException @@ -52,13 +48,13 @@ * Get the name of the JobListener. *

*/ - public String getName(); + String getName(); /** *

* Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} * is about to be executed (an associated {@link Trigger} - * has occured). + * has occurred). *

* *

@@ -68,19 +64,19 @@ * * @see #jobExecutionVetoed(JobExecutionContext) */ - public void jobToBeExecuted(JobExecutionContext context); + void jobToBeExecuted(JobExecutionContext context); /** *

* Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} * was about to be executed (an associated {@link Trigger} - * has occured), but a {@link TriggerListener} vetoed it's + * has occurred), but a {@link TriggerListener} vetoed it's * execution. *

* * @see #jobToBeExecuted(JobExecutionContext) */ - public void jobExecutionVetoed(JobExecutionContext context); + void jobExecutionVetoed(JobExecutionContext context); /** @@ -90,7 +86,7 @@ * triggered(xx) method has been called. *

*/ - public void jobWasExecuted(JobExecutionContext context, + void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException); } Index: 3rdParty_sources/quartz/org/quartz/JobPersistenceException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/JobPersistenceException.java (.../JobPersistenceException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/JobPersistenceException.java (.../JobPersistenceException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,20 +16,17 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; /** - *

* 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 { + + private static final long serialVersionUID = -8924958757341995694L; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -46,38 +43,17 @@ */ public JobPersistenceException(String msg) { super(msg); - setErrorCode(ERR_PERSISTENCE); } - /** - *

- * Create a JobPersistenceException with the given message - * and error code. - *

- */ - public JobPersistenceException(String msg, int errCode) { - super(msg, errCode); - } /** *

* Create a JobPersistenceException with the given message * and cause. *

*/ - public JobPersistenceException(String msg, Exception cause) { + public JobPersistenceException(String msg, Throwable cause) { super(msg, cause); - setErrorCode(ERR_PERSISTENCE); } - /** - *

- * Create a JobPersistenceException with the given message, - * cause and error code. - *

- */ - public JobPersistenceException(String msg, Exception cause, int errorCode) { - super(msg, cause, errorCode); - } - } Index: 3rdParty_sources/quartz/org/quartz/ListenerManager.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/ListenerManager.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/ListenerManager.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,277 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.util.List; + +/** + * Client programs may 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. Listeners can be associated with local schedulers through the + * {@link ListenerManager} interface. + * + *

Listener registration order is preserved, and hence notification of listeners + * will be in the order in which they were registered.

+ * + * @author jhouse + * @since 2.0 - previously listeners were managed directly on the Scheduler interface. + */ +public interface ListenerManager { + + /** + * Add the given {@link JobListener} to the Scheduler, + * and register it to receive events for all Jobs. + * + * Because no matchers are provided, the EverythingMatcher will be used. + * + * @see Matcher + * @see org.quartz.impl.matchers.EverythingMatcher + */ + public void addJobListener(JobListener jobListener); + + /** + * Add the given {@link JobListener} to the Scheduler, + * and register it to receive events for Jobs that are matched by the + * given Matcher. + * + * If no matchers are provided, the EverythingMatcher will be used. + * + * @see Matcher + * @see org.quartz.impl.matchers.EverythingMatcher + */ + public void addJobListener(JobListener jobListener, Matcher matcher); + + /** + * Add the given {@link JobListener} to the Scheduler, + * and register it to receive events for Jobs that are matched by ANY of the + * given Matchers. + * + * If no matchers are provided, the EverythingMatcher will be used. + * + * @see Matcher + * @see org.quartz.impl.matchers.EverythingMatcher + */ + public void addJobListener(JobListener jobListener, Matcher ... matchers); + + /** + * Add the given {@link JobListener} to the Scheduler, + * and register it to receive events for Jobs that are matched by ANY of the + * given Matchers. + * + * If no matchers are provided, the EverythingMatcher will be used. + * + * @see Matcher + * @see org.quartz.impl.matchers.EverythingMatcher + */ + public void addJobListener(JobListener jobListener, List> matchers); + + /** + * Add the given Matcher to the set of matchers for which the listener + * will receive events if ANY of the matchers match. + * + * @param listenerName the name of the listener to add the matcher to + * @param matcher the additional matcher to apply for selecting events + * @return true if the identified listener was found and updated + */ + public boolean addJobListenerMatcher(String listenerName, Matcher matcher); + + /** + * Remove the given Matcher to the set of matchers for which the listener + * will receive events if ANY of the matchers match. + * + * @param listenerName the name of the listener to add the matcher to + * @param matcher the additional matcher to apply for selecting events + * @return true if the given matcher was found and removed from the listener's list of matchers + */ + public boolean removeJobListenerMatcher(String listenerName, Matcher matcher); + + /** + * Set the set of Matchers for which the listener + * will receive events if ANY of the matchers match. + * + *

Removes any existing matchers for the identified listener!

+ * + * @param listenerName the name of the listener to add the matcher to + * @param matchers the matchers to apply for selecting events + * @return true if the given matcher was found and removed from the listener's list of matchers + */ + public boolean setJobListenerMatchers(String listenerName, List> matchers); + + /** + * Get the set of Matchers for which the listener + * will receive events if ANY of the matchers match. + * + * + * @param listenerName the name of the listener to add the matcher to + * @return the matchers registered for selecting events for the identified listener + */ + public List> getJobListenerMatchers(String listenerName); + + /** + * Remove the identified {@link JobListener} from the Scheduler. + * + * @return true if the identified listener was found in the list, and + * removed. + */ + public boolean removeJobListener(String name); + + /** + * Get a List containing all of the {@link JobListener}s in + * the Scheduler, in the order in which they were registered. + */ + public List getJobListeners(); + + /** + * Get the {@link JobListener} that has the given name. + */ + public JobListener getJobListener(String name); + + /** + * Add the given {@link TriggerListener} to the Scheduler, + * and register it to receive events for all Triggers. + * + * Because no matcher is provided, the EverythingMatcher will be used. + * + * @see Matcher + * @see org.quartz.impl.matchers.EverythingMatcher + */ + public void addTriggerListener(TriggerListener triggerListener); + + /** + * Add the given {@link TriggerListener} to the Scheduler, + * and register it to receive events for Triggers that are matched by the + * given Matcher. + * + * If no matcher is provided, the EverythingMatcher will be used. + * + * @see Matcher + * @see org.quartz.impl.matchers.EverythingMatcher + */ + public void addTriggerListener(TriggerListener triggerListener, Matcher matcher); + + /** + * Add the given {@link TriggerListener} to the Scheduler, + * and register it to receive events for Triggers that are matched by ANY of the + * given Matchers. + * + * If no matcher is provided, the EverythingMatcher will be used. + * + * @see Matcher + * @see org.quartz.impl.matchers.EverythingMatcher + */ + public void addTriggerListener(TriggerListener triggerListener, Matcher ... matchers); + + /** + * Add the given {@link TriggerListener} to the Scheduler, + * and register it to receive events for Triggers that are matched by ANY of the + * given Matchers. + * + * If no matcher is provided, the EverythingMatcher will be used. + * + * @see Matcher + * @see org.quartz.impl.matchers.EverythingMatcher + */ + public void addTriggerListener(TriggerListener triggerListener, List> matchers); + + /** + * Add the given Matcher to the set of matchers for which the listener + * will receive events if ANY of the matchers match. + * + * @param listenerName the name of the listener to add the matcher to + * @param matcher the additional matcher to apply for selecting events + * @return true if the identified listener was found and updated + */ + public boolean addTriggerListenerMatcher(String listenerName, Matcher matcher); + + /** + * Remove the given Matcher to the set of matchers for which the listener + * will receive events if ANY of the matchers match. + * + * @param listenerName the name of the listener to add the matcher to + * @param matcher the additional matcher to apply for selecting events + * @return true if the given matcher was found and removed from the listener's list of matchers + */ + public boolean removeTriggerListenerMatcher(String listenerName, Matcher matcher); + + /** + * Set the set of Matchers for which the listener + * will receive events if ANY of the matchers match. + * + *

Removes any existing matchers for the identified listener!

+ * + * @param listenerName the name of the listener to add the matcher to + * @param matchers the matchers to apply for selecting events + * @return true if the given matcher was found and removed from the listener's list of matchers + */ + public boolean setTriggerListenerMatchers(String listenerName, List> matchers); + + /** + * Get the set of Matchers for which the listener + * will receive events if ANY of the matchers match. + * + * + * @param listenerName the name of the listener to add the matcher to + * @return the matchers registered for selecting events for the identified listener + */ + public List> getTriggerListenerMatchers( String listenerName); + + /** + * Remove the identified {@link TriggerListener} from the Scheduler. + * + * @return true if the identified listener was found in the list, and + * removed. + */ + public boolean removeTriggerListener(String name); + + /** + * Get a List containing all of the {@link TriggerListener}s + * in the Scheduler, in the order in which they were registered. + */ + public List getTriggerListeners(); + + /** + * Get the {@link TriggerListener} that has the given name. + */ + public TriggerListener getTriggerListener(String name); + + /** + * Register the given {@link SchedulerListener} with the + * Scheduler. + */ + public void addSchedulerListener(SchedulerListener schedulerListener); + + /** + * Remove the given {@link SchedulerListener} from the + * Scheduler. + * + * @return true if the identified listener was found in the list, and + * removed. + */ + public boolean removeSchedulerListener(SchedulerListener schedulerListener); + + /** + * Get a List containing all of the {@link SchedulerListener}s + * registered with the Scheduler, in the order in which they were registered. + */ + public List getSchedulerListeners(); + +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/Matcher.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/Matcher.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/Matcher.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,38 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.io.Serializable; + +import org.quartz.utils.Key; + +/** + * Matchers can be used in various {@link Scheduler} API methods to + * select the entities that should be operated upon. + * + * @author jhouse + * @since 2.0 + */ +public interface Matcher> extends Serializable { + + boolean isMatch(T key); + + public int hashCode(); + + public boolean equals(Object obj); +} Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/NthIncludedDayTrigger.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/ObjectAlreadyExistsException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/ObjectAlreadyExistsException.java (.../ObjectAlreadyExistsException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/ObjectAlreadyExistsException.java (.../ObjectAlreadyExistsException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,22 +16,19 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; /** - *

* An exception that is thrown to indicate that an attempt to store a new * object (i.e. {@link org.quartz.JobDetail},{@link Trigger} * or {@link Calendar}) in a {@link Scheduler} * failed, because one with the same name & group already exists. - *

* * @author James House */ public class ObjectAlreadyExistsException extends JobPersistenceException { + + private static final long serialVersionUID = -558301282071659896L; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -63,8 +60,7 @@ *

*/ public ObjectAlreadyExistsException(JobDetail offendingJob) { - super("Unable to store Job with name: '" + offendingJob.getName() - + "' and group: '" + offendingJob.getGroup() + super("Unable to store Job : '" + offendingJob.getKey() + "', because one already exists with this identification."); } @@ -81,8 +77,8 @@ */ public ObjectAlreadyExistsException(Trigger offendingTrigger) { super("Unable to store Trigger with name: '" - + offendingTrigger.getName() + "' and group: '" - + offendingTrigger.getGroup() + + offendingTrigger.getKey().getName() + "' and group: '" + + offendingTrigger.getKey().getGroup() + "', because one already exists with this identification."); } Index: 3rdParty_sources/quartz/org/quartz/PersistJobDataAfterExecution.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/PersistJobDataAfterExecution.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/PersistJobDataAfterExecution.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,44 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An annotation that marks a {@link Job} class as one that makes updates to its + * {@link JobDataMap} during execution, and wishes the scheduler to re-store the + * JobDataMap when execution completes. + * + *

Jobs that are marked with this annotation should also seriously consider + * using the {@link DisallowConcurrentExecution} annotation, to avoid data + * storage race conditions with concurrently executing job instances.

+ * + * @see DisallowConcurrentExecution + * + * @author jhouse + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface PersistJobDataAfterExecution { + +} Index: 3rdParty_sources/quartz/org/quartz/ScheduleBuilder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/ScheduleBuilder.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/ScheduleBuilder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,26 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import org.quartz.spi.MutableTrigger; + +public abstract class ScheduleBuilder { + + protected abstract MutableTrigger build(); + +} Index: 3rdParty_sources/quartz/org/quartz/Scheduler.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/Scheduler.java (.../Scheduler.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/Scheduler.java (.../Scheduler.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,26 +16,25 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Set; +import org.quartz.Trigger.TriggerState; +import org.quartz.impl.matchers.GroupMatcher; import org.quartz.spi.JobFactory; +import org.quartz.utils.Key; /** - *

* This is the main interface of a Quartz Scheduler. - *

* *

- * A Scheduler maintains a registery of {@link org.quartz.JobDetail} - * s and {@link Trigger}s. Once registered, the Scheduler - * is responible for executing Job s when their associated + * A Scheduler maintains a registry of {@link org.quartz.JobDetail}s + * and {@link Trigger}s. Once registered, the Scheduler + * is responsible for executing Job s when their associated * Trigger s fire (when their scheduled time arrives). *

* @@ -87,7 +86,8 @@ * 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. + * Scheduler events and errors. Listeners can be associated with + * local schedulers through the {@link ListenerManager} interface. *

* *

@@ -97,7 +97,9 @@ * * @see Job * @see JobDetail + * @see JobBuilder * @see Trigger + * @see TriggerBuilder * @see JobListener * @see TriggerListener * @see SchedulerListener @@ -116,40 +118,78 @@ */ /** - *

- * A (possibly) usefull constant that can be used for specifying the group + * A (possibly) useful constant that can be used for specifying the group * that Job and Trigger instances belong to. - *

*/ - public static final String DEFAULT_GROUP = "DEFAULT"; + String DEFAULT_GROUP = Key.DEFAULT_GROUP; /** - *

* A constant Trigger group name used internally by the * scheduler - clients should not use the value of this constant - * ("MANUAL_TRIGGER") for thename of a Trigger's group. - *

+ * ("RECOVERING_JOBS") for the name of a Trigger's group. + * + * @see org.quartz.JobDetail#requestsRecovery() */ - public static final String DEFAULT_MANUAL_TRIGGERS = "MANUAL_TRIGGER"; + String DEFAULT_RECOVERY_GROUP = "RECOVERING_JOBS"; /** - *

* A constant Trigger group name used internally by the * scheduler - clients should not use the value of this constant - * ("RECOVERING_JOBS") for thename of a Trigger's group. - *

+ * ("FAILED_OVER_JOBS") for the name of a Trigger's group. + * + * @see org.quartz.JobDetail#requestsRecovery() */ - public static final String DEFAULT_RECOVERY_GROUP = "RECOVERING_JOBS"; + String DEFAULT_FAIL_OVER_GROUP = "FAILED_OVER_JOBS"; + /** - *

- * A constant Trigger group name used internally by the - * scheduler - clients should not use the value of this constant - * ("FAILED_OVER_JOBS") for thename of a Trigger's group. - *

+ * A constant 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 org.quartz.JobDetail#requestsRecovery() */ - public static final String DEFAULT_FAIL_OVER_GROUP = "FAILED_OVER_JOBS"; + 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 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 + * 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. + * + *

Note that this is the time the original firing actually occurred, + * which may be different from the scheduled fire time - as a trigger doesn't + * always fire exactly on time.

+ * + * @see org.quartz.JobDetail#requestsRecovery() + */ + String FAILED_JOB_ORIGINAL_TRIGGER_FIRETIME_IN_MILLISECONDS = "QRTZ_FAILED_JOB_ORIG_TRIGGER_FIRETIME_IN_MILLISECONDS_AS_STRING"; + + /** + * 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. + * + *

Note that this is the time the original firing was scheduled for, + * which may be different from the actual firing time - as a trigger doesn't + * always fire exactly on time.

+ * + * @see org.quartz.JobDetail#requestsRecovery() + */ + String FAILED_JOB_ORIGINAL_TRIGGER_SCHEDULED_FIRETIME_IN_MILLISECONDS = "QRTZ_FAILED_JOB_ORIG_TRIGGER_SCHEDULED_FIRETIME_IN_MILLISECONDS_AS_STRING"; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -159,39 +199,31 @@ */ /** - *

* Returns the name of the Scheduler. - *

*/ - public String getSchedulerName() throws SchedulerException; + String getSchedulerName() throws SchedulerException; /** - *

* Returns the instance Id of the Scheduler. - *

*/ - public String getSchedulerInstanceId() throws SchedulerException; + String getSchedulerInstanceId() throws SchedulerException; /** - *

* Returns the SchedulerContext of the Scheduler. - *

*/ - public SchedulerContext getContext() throws SchedulerException; + SchedulerContext getContext() throws SchedulerException; /////////////////////////////////////////////////////////////////////////// /// - /// Schedululer State Management Methods + /// Scheduler State Management Methods /// /////////////////////////////////////////////////////////////////////////// - + /** - *

* 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 @@ -201,17 +233,48 @@ * @throws SchedulerException * if shutdown() has been called, or there is an * error within the Scheduler. - * - * @see #standby - * @see #shutdown + * + * @see #startDelayed(int) + * @see #standby() + * @see #shutdown() */ - public void start() throws SchedulerException; + 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 + * if shutdown() 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. + * *

- * Temporarily halts the Scheduler's firing of {@link Trigger}s. + * 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() + */ + boolean isStarted() throws SchedulerException; + + /** + * 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 @@ -227,50 +290,32 @@ * @see #start() * @see #pauseAll() */ - public void standby() throws SchedulerException; + void standby() throws SchedulerException; /** - * @deprecated replaced by better-named standby() method. - * @see #standby() - */ - public void pause() throws SchedulerException; - - /** - *

* Reports whether the Scheduler is in stand-by mode. - *

* * @see #standby() * @see #start() */ - public boolean isInStandbyMode() throws SchedulerException; + boolean isInStandbyMode() throws SchedulerException; /** - * @deprecated - * @see #isInStandbyMode() - */ - public boolean isPaused() throws SchedulerException; - - /** - *

* 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) */ - public void shutdown() throws SchedulerException; + 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. @@ -282,33 +327,33 @@ * * @see #shutdown */ - public void shutdown(boolean waitForJobsToComplete) - throws SchedulerException; + void shutdown(boolean waitForJobsToComplete) + throws SchedulerException; /** - *

* Reports whether the Scheduler has been shutdown. - *

*/ - public boolean isShutdown() throws SchedulerException; + boolean isShutdown() throws SchedulerException; /** - *

- * Get a SchedulerMetaData object describiing the settings + * Get a SchedulerMetaData object describing 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. *

*/ - public SchedulerMetaData getMetaData() throws SchedulerException; + SchedulerMetaData getMetaData() throws SchedulerException; /** - *

* Return a list of JobExecutionContext objects that - * represent all currently executing Jobs. + * 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. *

* *

@@ -320,37 +365,46 @@ * * @see JobExecutionContext */ - public List getCurrentlyExecutingJobs() throws SchedulerException; + List getCurrentlyExecutingJobs() throws SchedulerException; /** - *

* 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. + * give the opportunity for dependency injection. *

* - * @see org.quart.spi.JobFactory - * @throws SchedulerException + * @see org.quartz.spi.JobFactory */ - public void setJobFactory(JobFactory factory) throws SchedulerException; + void setJobFactory(JobFactory factory) throws SchedulerException; + + /** + * Get a reference to the scheduler's ListenerManager, + * through which listeners may be registered. + * + * @return the scheduler's ListenerManager + * @throws SchedulerException if the scheduler is not local + * @see ListenerManager + * @see JobListener + * @see TriggerListener + * @see SchedulerListener + */ + ListenerManager getListenerManager() throws SchedulerException; + /////////////////////////////////////////////////////////////////////////// /// /// Scheduling-related Methods /// /////////////////////////////////////////////////////////////////////////// /** - *

* Add the given {@link 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 @@ -361,272 +415,327 @@ * if the Job or Trigger cannot be added to the Scheduler, or * there is an internal Scheduler error. */ - public Date scheduleJob(JobDetail jobDetail, Trigger trigger) - throws SchedulerException; + Date scheduleJob(JobDetail jobDetail, Trigger trigger) + throws SchedulerException; /** - *

* Schedule the given {@link org.quartz.Trigger} with the * Job identified by the Trigger's settings. - *

* * @throws SchedulerException * if the indicated Job does not exist, or the Trigger cannot be * added to the Scheduler, or there is an internal Scheduler * error. */ - public Date scheduleJob(Trigger trigger) throws SchedulerException; + Date scheduleJob(Trigger trigger) throws SchedulerException; /** - *

+ * Schedule all of the given jobs with the related set of triggers. + * + *

If any of the given jobs or triggers already exist (or more + * specifically, if the keys are not unique) and the replace + * parameter is not set to true then an exception will be thrown.

+ * + * @throws ObjectAlreadyExistsException if the job/trigger keys + * are not unique and the replace flag is not set to true. + */ + void scheduleJobs(Map> triggersAndJobs, boolean replace) throws SchedulerException; + + /** + * Schedule the given job with the related set of triggers. + * + *

If any of the given job or triggers already exist (or more + * specifically, if the keys are not unique) and the replace + * parameter is not set to true then an exception will be thrown.

+ * + * @throws ObjectAlreadyExistsException if the job/trigger keys + * are not unique and the replace flag is not set to true. + */ + void scheduleJob(JobDetail jobDetail, Set triggersForJob, boolean replace) throws SchedulerException; + + /** * Remove the indicated {@link Trigger} from the scheduler. - *

+ * + *

If the related job does not have any other triggers, and the job is + * not durable, then the job will also be deleted.

*/ - public boolean unscheduleJob(String triggerName, String groupName) - throws SchedulerException; + boolean unscheduleJob(TriggerKey triggerKey) + throws SchedulerException; /** - *

+ * Remove all of the indicated {@link Trigger}s from the scheduler. + * + *

If the related job does not have any other triggers, and the job is + * not durable, then the job will also be deleted.

+ * + *

Note that while this bulk operation is likely more efficient than + * invoking unscheduleJob(TriggerKey triggerKey) several + * times, it may have the adverse affect of holding data locks for a + * single long duration of time (rather than lots of small durations + * of time).

+ */ + boolean unscheduleJobs(List triggerKeys) + throws SchedulerException; + + /** * Remove (delete) the {@link org.quartz.Trigger} with the - * given name, and store the new given one - which must be associated + * given key, 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. - *

* - * @param triggerName - * The name of the Trigger to be replaced. - * @param groupName - * The group name of the Trigger to be replaced. + * @param triggerKey identity of the trigger to replace * @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. + * name & group was not found and removed from the store (and the + * new trigger is therefore not stored), otherwise + * the first fire time of the newly scheduled trigger is returned. */ - public Date rescheduleJob(String triggerName, - String groupName, Trigger newTrigger) throws SchedulerException; - + Date rescheduleJob(TriggerKey triggerKey, 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. *

- * + * + * @see #addJob(JobDetail, boolean, boolean) + * * @throws SchedulerException * if there is an internal Scheduler error, or if the Job is not * durable, or a Job with the same name already exists, and * replace is false. */ - public void addJob(JobDetail jobDetail, boolean replace) - throws SchedulerException; + void addJob(JobDetail jobDetail, boolean replace) + 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. + * *

+ * With the storeNonDurableWhileAwaitingScheduling parameter + * set to true, a non-durable job can be stored. Once it is + * scheduled, it will resume normal non-durable behavior (i.e. be deleted + * once there are no remaining associated triggers). + *

+ * + * @throws SchedulerException + * if there is an internal Scheduler error, or if the Job is not + * durable, or a Job with the same name already exists, and + * replace is false. + */ + void addJob(JobDetail jobDetail, boolean replace, boolean storeNonDurableWhileAwaitingScheduling) + throws SchedulerException; + + /** * Delete the identified Job from the Scheduler - and any * associated Triggers. - *

* * @return true if the Job was found and deleted. * @throws SchedulerException * if there is an internal Scheduler error. */ - public boolean deleteJob(String jobName, String groupName) - throws SchedulerException; + boolean deleteJob(JobKey jobKey) + throws SchedulerException; /** - *

- * Trigger the identified {@link org.quartz.JobDetail} - * (execute it now) - the generated trigger will be non-volatile. - *

+ * Delete the identified Jobs from the Scheduler - and any + * associated Triggers. + * + *

Note that while this bulk operation is likely more efficient than + * invoking deleteJob(JobKey jobKey) several + * times, it may have the adverse affect of holding data locks for a + * single long duration of time (rather than lots of small durations + * of time).

+ * + * @return true if all of the Jobs were found and deleted, false if + * one or more were not deleted. + * @throws SchedulerException + * if there is an internal Scheduler error. */ - public void triggerJob(String jobName, String groupName) - throws SchedulerException; - + boolean deleteJobs(List jobKeys) + throws SchedulerException; + /** - *

* Trigger the identified {@link org.quartz.JobDetail} - * (execute it now) - the generated trigger will be volatile. - *

+ * (execute it now). */ - public void triggerJobWithVolatileTrigger(String jobName, String groupName) - throws SchedulerException; + void triggerJob(JobKey jobKey) + throws SchedulerException; /** - *

* Trigger the identified {@link org.quartz.JobDetail} - * (execute it now) - the generated trigger will be non-volatile. - *

+ * (execute it now). * - * @param jobName the name of the Job to trigger - * @param groupName the group name of the Job to trigger * @param data the (possibly null) JobDataMap to be * associated with the trigger that fires the job immediately. */ - public void triggerJob(String jobName, String groupName, JobDataMap data) - throws SchedulerException; + void triggerJob(JobKey jobKey, JobDataMap data) + throws SchedulerException; /** - *

- * Trigger the identified {@link org.quartz.JobDetail} - * (execute it now) - the generated trigger will be volatile. - *

- * - * @param jobName the name of the Job to trigger - * @param groupName the group name of the Job to trigger - * @param data the (possibly null) JobDataMap to be - * associated with the trigger that fires the job immediately. - */ - public void triggerJobWithVolatileTrigger(String jobName, String groupName, JobDataMap data) - throws SchedulerException; - - /** - *

* Pause the {@link org.quartz.JobDetail} with the given - * name - by pausing all of its current Triggers. - *

+ * key - by pausing all of its current Triggers. * - * @see #resumeJob(String, String) + * @see #resumeJob(JobKey) */ - public void pauseJob(String jobName, String groupName) - throws SchedulerException; + void pauseJob(JobKey jobKey) + throws SchedulerException; /** - *

* Pause all of the {@link org.quartz.JobDetail}s in the - * given group - by pausing all of their Triggers. - *

- * + * matching groups - by pausing all of their Triggers. + * *

- * 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. + * The Scheduler will "remember" the groups paused, and impose the + * pause on any new jobs that are added to any of those groups + * until it is resumed. *

* - * @see #resumeJobGroup(String) + *

NOTE: There is a limitation that only exactly matched groups + * can be remembered as paused. For example, if there are pre-existing + * job in groups "aaa" and "bbb" and a matcher is given to pause + * groups that start with "a" then the group "aaa" will be remembered + * as paused and any subsequently added jobs in group "aaa" will be paused, + * however if a job is added to group "axx" it will not be paused, + * as "axx" wasn't known at the time the "group starts with a" matcher + * was applied. HOWEVER, if there are pre-existing groups "aaa" and + * "bbb" and a matcher is given to pause the group "axx" (with a + * group equals matcher) then no jobs will be paused, but it will be + * remembered that group "axx" is paused and later when a job is added + * in that group, it will become paused.

+ * + * @param matcher The matcher to evaluate against know groups + * @throws SchedulerException On error + * @see #resumeJobs(org.quartz.impl.matchers.GroupMatcher) */ - public void pauseJobGroup(String groupName) throws SchedulerException; + void pauseJobs(GroupMatcher matcher) throws SchedulerException; /** - *

- * Pause the {@link Trigger} with the given name. - *

+ * Pause the {@link Trigger} with the given key. * - * @see #resumeTrigger(String, String) + * @see #resumeTrigger(TriggerKey) */ - public void pauseTrigger(String triggerName, String groupName) - throws SchedulerException; + void pauseTrigger(TriggerKey triggerKey) + throws SchedulerException; /** - *

- * Pause all of the {@link Trigger}s in the given group. - *

+ * Pause all of the {@link Trigger}s in the groups matching. * *

- * 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. + * The Scheduler will "remember" all the groups paused, and impose the + * pause on any new triggers that are added to any of those groups + * until it is resumed. *

* - * @see #resumeTriggerGroup(String) + *

NOTE: There is a limitation that only exactly matched groups + * can be remembered as paused. For example, if there are pre-existing + * triggers in groups "aaa" and "bbb" and a matcher is given to pause + * groups that start with "a" then the group "aaa" will be remembered as + * paused and any subsequently added triggers in that group be paused, + * however if a trigger is added to group "axx" it will not be paused, + * as "axx" wasn't known at the time the "group starts with a" matcher + * was applied. HOWEVER, if there are pre-existing groups "aaa" and + * "bbb" and a matcher is given to pause the group "axx" (with a + * group equals matcher) then no triggers will be paused, but it will be + * remembered that group "axx" is paused and later when a trigger is added + * in that group, it will become paused.

+ * + * @param matcher The matcher to evaluate against know groups + * @throws SchedulerException + * @see #resumeTriggers(org.quartz.impl.matchers.GroupMatcher) */ - public void pauseTriggerGroup(String groupName) throws SchedulerException; + void pauseTriggers(GroupMatcher matcher) throws SchedulerException; /** - *

* Resume (un-pause) the {@link org.quartz.JobDetail} with - * the given name. - *

+ * the given key. * *

* If any of the Job'sTrigger s missed one * or more fire-times, then the Trigger's misfire * instruction will be applied. *

* - * @see #pauseJob(String, String) + * @see #pauseJob(JobKey) */ - public void resumeJob(String jobName, String groupName) - throws SchedulerException; + void resumeJob(JobKey jobKey) + throws SchedulerException; /** - *

* Resume (un-pause) all of the {@link org.quartz.JobDetail}s - * in the given group. - *

+ * in matching groups. * *

* 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. *

* - * @see #pauseJobGroup(String) + * @param matcher The matcher to evaluate against known paused groups + * @throws SchedulerException On error + * @see #pauseJobs(GroupMatcher) */ - public void resumeJobGroup(String groupName) throws SchedulerException; + void resumeJobs(GroupMatcher matcher) throws SchedulerException; /** - *

* Resume (un-pause) the {@link Trigger} with the given - * name. - *

+ * key. * *

* If the Trigger missed one or more fire-times, then the * Trigger's misfire instruction will be applied. *

* - * @see #pauseTrigger(String, String) + * @see #pauseTrigger(TriggerKey) */ - public void resumeTrigger(String triggerName, String groupName) - throws SchedulerException; + void resumeTrigger(TriggerKey triggerKey) + throws SchedulerException; /** - *

- * Resume (un-pause) all of the {@link Trigger}s in the - * given group. - *

+ * Resume (un-pause) all of the {@link Trigger}s in matching groups. * *

* If any Trigger missed one or more fire-times, then the * Trigger's misfire instruction will be applied. *

* - * @see #pauseTriggerGroup(String) + * @param matcher The matcher to evaluate against know paused groups + * @throws SchedulerException On error + * @see #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) */ - public void resumeTriggerGroup(String groupName) throws SchedulerException; + void resumeTriggers(GroupMatcher matcher) throws SchedulerException; /** - *

* 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. *

* * @see #resumeAll() - * @see #pauseTriggerGroup(String) + * @see #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) * @see #standby() */ - public void pauseAll() throws SchedulerException; + void pauseAll() throws SchedulerException; /** - *

* Resume (un-pause) all triggers - similar to calling * resumeTriggerGroup(group) on every group. - *

* *

* If any Trigger missed one or more fire-times, then the @@ -635,95 +744,89 @@ * * @see #pauseAll() */ - public void resumeAll() throws SchedulerException; + void resumeAll() throws SchedulerException; /** - *

* Get the names of all known {@link org.quartz.JobDetail} * groups. - *

*/ - public String[] getJobGroupNames() throws SchedulerException; + List getJobGroupNames() throws SchedulerException; /** - *

- * Get the names of all the {@link org.quartz.JobDetail}s - * in the given group. - *

+ * Get the keys of all the {@link org.quartz.JobDetail}s + * in the matching groups. + * @param matcher Matcher to evaluate against known groups + * @return Set of all keys matching + * @throws SchedulerException On error */ - public String[] getJobNames(String groupName) throws SchedulerException; + Set getJobKeys(GroupMatcher matcher) throws SchedulerException; /** - *

* Get all {@link Trigger} s that are associated with the * identified {@link org.quartz.JobDetail}. + * + *

The returned Trigger objects will be snap-shots of the actual stored + * triggers. If you wish to modify a trigger, you must re-store the + * trigger afterward (e.g. see {@link #rescheduleJob(TriggerKey, Trigger)}). *

+ * */ - public Trigger[] getTriggersOfJob(String jobName, String groupName) - throws SchedulerException; + List getTriggersOfJob(JobKey jobKey) + throws SchedulerException; /** - *

* Get the names of all known {@link Trigger} groups. - *

*/ - public String[] getTriggerGroupNames() throws SchedulerException; + List getTriggerGroupNames() throws SchedulerException; /** - *

* Get the names of all the {@link Trigger}s in the given * group. - *

+ * @param matcher Matcher to evaluate against known groups + * @return List of all keys matching + * @throws SchedulerException On error */ - public String[] getTriggerNames(String groupName) throws SchedulerException; + Set getTriggerKeys(GroupMatcher matcher) throws SchedulerException; /** - *

* Get the names of all {@link Trigger} groups that are paused. - *

- * - * @return - * @throws SchedulerException */ - public Set getPausedTriggerGroups() throws SchedulerException; + Set getPausedTriggerGroups() throws SchedulerException; /** - *

* Get the {@link JobDetail} for the Job - * instance with the given name and group. + * instance with the given key. + * + *

The returned JobDetail object will be a snap-shot of the actual stored + * JobDetail. If you wish to modify the JobDetail, you must re-store the + * JobDetail afterward (e.g. see {@link #addJob(JobDetail, boolean)}). *

+ * */ - public JobDetail getJobDetail(String jobName, String jobGroup) - throws SchedulerException; + JobDetail getJobDetail(JobKey jobKey) + throws SchedulerException; /** - *

- * Get the {@link Trigger} instance with the given name and - * group. + * Get the {@link Trigger} instance with the given key. + * + *

The returned Trigger object will be a snap-shot of the actual stored + * trigger. If you wish to modify the trigger, you must re-store the + * trigger afterward (e.g. see {@link #rescheduleJob(TriggerKey, Trigger)}). *

*/ - public Trigger getTrigger(String triggerName, String triggerGroup) - throws SchedulerException; + Trigger getTrigger(TriggerKey triggerKey) + throws SchedulerException; /** - *

* Get the current state of the identified {@link Trigger}. - *

* - * @see Trigger#STATE_NORMAL - * @see Trigger#STATE_PAUSED - * @see Trigger#STATE_COMPLETE - * @see Trigger#STATE_ERROR - * @see Trigger#STATE_BLOCKED - * @see Trigger#STATE_NONE + * @see Trigger.TriggerState */ - public int getTriggerState(String triggerName, String triggerGroup) - throws SchedulerException; + TriggerState getTriggerState(TriggerKey triggerKey) + throws SchedulerException; /** - *

* Add (register) the given Calendar to the Scheduler. - *

* * @param updateTriggers whether or not to update existing triggers that * referenced the already existing calendar so that they are 'correct' @@ -735,40 +838,39 @@ * the same name already exists, and replace is * false. */ - public void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) - throws SchedulerException; + void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) + throws SchedulerException; /** - *

* Delete the identified Calendar from the Scheduler. + * + *

+ * If removal of the Calendar would result in + * Triggers pointing to non-existent calendars, then a + * SchedulerException will be thrown. *

* * @return true if the Calendar was found and deleted. * @throws SchedulerException - * if there is an internal Scheduler error. + * if there is an internal Scheduler error, or one or more + * triggers reference the calendar */ - public boolean deleteCalendar(String calName) throws SchedulerException; + boolean deleteCalendar(String calName) throws SchedulerException; /** - *

* Get the {@link Calendar} instance with the given name. - *

*/ - public Calendar getCalendar(String calName) throws SchedulerException; + Calendar getCalendar(String calName) throws SchedulerException; /** - *

* Get the names of all registered {@link Calendar}s. - *

*/ - public String[] getCalendarNames() throws SchedulerException; + List getCalendarNames() throws SchedulerException; /** - *

- * Request the interruption of all currently executing instances of the - * identified Job, which must be an implementor of the - * InterruptableJob interface. - *

+ * 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, @@ -780,198 +882,73 @@ *

* *

- * 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 + * @return true if 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() + * @see #interrupt(String) */ - public boolean interrupt(String jobName, String groupName) throws UnableToInterruptJobException; + boolean interrupt(JobKey jobKey) throws UnableToInterruptJobException; - /////////////////////////////////////////////////////////////////////////// - /// - /// Listener-related Methods - /// - /////////////////////////////////////////////////////////////////////////// - /** - *

- * Add the given {@link JobListener} to the Scheduler's - * global list. - *

+ * Request the interruption, within this Scheduler instance, of the + * identified executing Job instance, which + * must be an implementor of the InterruptableJob interface. * *

- * Listeners in the 'global' list receive notification of execution events - * for ALL {@link org.quartz.JobDetail}s. + * 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. *

- */ - public void addGlobalJobListener(JobListener jobListener) - throws SchedulerException; - - /** - *

- * Add the given {@link JobListener} to the Scheduler's - * list, of registered JobListeners. - */ - public void addJobListener(JobListener jobListener) - throws SchedulerException; - - /** - *

- * Remove the given {@link JobListener} from the Scheduler's - * list of global listeners. - *

* - * @return true if the identifed listener was found in the list, and - * removed. + * @param fireInstanceId the unique identifier of the job instance to + * be interrupted (see {@link JobExecutionContext#getFireInstanceId()} + * @return true if the identified job instance 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() + * @see JobExecutionContext#getFireInstanceId() + * @see #interrupt(JobKey) */ - public boolean removeGlobalJobListener(JobListener jobListener) - throws SchedulerException; - + boolean interrupt(String fireInstanceId) throws UnableToInterruptJobException; + /** - *

- * Remove the identifed {@link JobListener} from the Scheduler's - * list of registered listeners. - *

+ * Determine whether a {@link Job} with the given identifier already + * exists within the scheduler. * - * @return true if the identifed listener was found in the list, and - * removed. + * @param jobKey the identifier to check for + * @return true if a Job exists with the given identifier + * @throws SchedulerException */ - public boolean removeJobListener(String name) throws SchedulerException; - + boolean checkExists(JobKey jobKey) throws SchedulerException; + /** - *

- * Get a List containing all of the {@link JobListener} s in - * the Scheduler'sglobal list. - *

- */ - public List getGlobalJobListeners() throws SchedulerException; - - /** - *

- * Get a Set containing the names of all the non-global{@link JobListener} - * s registered with the Scheduler. - *

- */ - public Set getJobListenerNames() throws SchedulerException; - - /** - *

- * Get the non-global{@link JobListener} that has - * the given name. - *

- */ - public JobListener getJobListener(String name) throws SchedulerException; - - /** - *

- * Add the given {@link TriggerListener} to the Scheduler's - * global list. - *

+ * Determine whether a {@link Trigger} with the given identifier already + * exists within the scheduler. * - *

- * Listeners in the 'global' list receive notification of execution events - * for ALL {@link Trigger}s. - *

+ * @param triggerKey the identifier to check for + * @return true if a Trigger exists with the given identifier + * @throws SchedulerException */ - public void addGlobalTriggerListener(TriggerListener triggerListener) - throws SchedulerException; - + boolean checkExists(TriggerKey triggerKey) throws SchedulerException; + /** - *

- * Add the given {@link TriggerListener} to the Scheduler's - * list, of registered TriggerListeners. - */ - public void addTriggerListener(TriggerListener triggerListener) - throws SchedulerException; - - /** - *

- * Remove the given {@link TriggerListener} from the Scheduler's - * list of global listeners. - *

+ * Clears (deletes!) all scheduling data - all {@link Job}s, {@link Trigger}s + * {@link Calendar}s. * - * @return true if the identifed listener was found in the list, and - * removed. + * @throws SchedulerException */ - public boolean removeGlobalTriggerListener(TriggerListener triggerListener) - throws SchedulerException; + void clear() throws SchedulerException; - /** - *

- * Remove the identifed {@link TriggerListener} from the - * Scheduler's list of registered listeners. - *

- * - * @return true if the identifed listener was found in the list, and - * removed. - */ - public boolean removeTriggerListener(String name) throws SchedulerException; - /** - *

- * Get a List containing all of the {@link TriggerListener} - * s in the Scheduler'sglobal list. - *

- */ - public List getGlobalTriggerListeners() throws SchedulerException; - - /** - *

- * Get a Set containing the names of all the non-global{@link TriggerListener} - * s registered with the Scheduler. - *

- */ - public Set getTriggerListenerNames() throws SchedulerException; - - /** - *

- * Get the non-global{@link TriggerListener} that - * has the given name. - *

- */ - public TriggerListener getTriggerListener(String name) - throws SchedulerException; - - /** - *

- * Register the given {@link SchedulerListener} with the - * Scheduler. - *

- */ - public void addSchedulerListener(SchedulerListener schedulerListener) - throws SchedulerException; - - /** - *

- * Remove the given {@link SchedulerListener} from the - * Scheduler. - *

- * - * @return true if the identifed listener was found in the list, and - * removed. - */ - public boolean removeSchedulerListener(SchedulerListener schedulerListener) - throws SchedulerException; - - /** - *

- * Get a List containing all of the {@link SchedulerListener} - * s registered with the Scheduler. - *

- */ - public List getSchedulerListeners() throws SchedulerException; - - } Index: 3rdParty_sources/quartz/org/quartz/SchedulerConfigException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/SchedulerConfigException.java (.../SchedulerConfigException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/SchedulerConfigException.java (.../SchedulerConfigException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,21 +16,18 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; /** - *

* An exception that is thrown to indicate that there is a misconfiguration of * the SchedulerFactory- or one of the components it * configures. - *

* * @author James House */ public class SchedulerConfigException extends SchedulerException { + + private static final long serialVersionUID = -5921239824646083098L; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -46,7 +43,7 @@ *

*/ public SchedulerConfigException(String msg) { - super(msg, ERR_BAD_CONFIGURATION); + super(msg); } /** @@ -55,9 +52,8 @@ * and cause. *

*/ - public SchedulerConfigException(String msg, Exception cause) { + public SchedulerConfigException(String msg, Throwable cause) { super(msg, cause); - setErrorCode(ERR_BAD_CONFIGURATION); } } Index: 3rdParty_sources/quartz/org/quartz/SchedulerContext.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/SchedulerContext.java (.../SchedulerContext.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/SchedulerContext.java (.../SchedulerContext.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,361 +16,46 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; import java.io.Serializable; -import java.util.Iterator; import java.util.Map; -import org.quartz.utils.DirtyFlagMap; +import org.quartz.utils.StringKeyDirtyFlagMap; /** - *

* 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 propagates + * data in SchedulerContext between instances of proxies to a + * single scheduler instance - i.e. if Quartz is being used via RMI. + *

+ * * @see Scheduler#getContext * * @author James House */ -public class SchedulerContext extends DirtyFlagMap implements Serializable { - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - private boolean allowsTransientData = false; - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Constructors. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - +public class SchedulerContext extends StringKeyDirtyFlagMap implements Serializable { + + private static final long serialVersionUID = -6659641334616491764L; + /** - *

- * Create an empty JobDataMap. - *

+ * Create an empty SchedulerContext. */ public SchedulerContext() { super(15); } /** - *

- * Create a JobDataMap with the given data. - *

+ * Create a SchedulerContext with the given data. */ - public SchedulerContext(Map map) { + public SchedulerContext(Map map) { this(); - - putAll(map); + @SuppressWarnings("unchecked") // param must be a String key map. + Map mapTyped = (Map)map; + putAll(mapTyped); } - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - /** - *

- * Tell the SchedulerContext that it should allow non- - * Serializable data. - *

- * - *

- * Future versions of Quartz may make distinctions on how it propogates - * data in the SchedulerContext between instances of proxies to a single - * scheduler instance - i.e. if Quartz is being used via RMI. - *

- */ - 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; - } - - public boolean getAllowsTransientData() { - return allowsTransientData; - } - - 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; - } - - /** - *

- * Nulls-out any data values that are non-Serializable. - *

- */ - 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 SchedulerContext. - *

- * - *

- * All keys must be Strings. - *

- */ - public void putAll(Map map) { - Iterator itr = map.keySet().iterator(); - while (itr.hasNext()) { - Object key = itr.next(); - Object val = map.get(key); - - put(key, val); - // will throw IllegalArgumentException if value not serilizable - } - } - - /** - *

- * Adds the given int value to the SchedulerContext. - *

- */ - public void put(String key, int value) { - super.put(key, new Integer(value)); - } - - /** - *

- * Adds the given long value to the SchedulerContext. - *

- */ - public void put(String key, long value) { - super.put(key, new Long(value)); - } - - /** - *

- * Adds the given float value to the SchedulerContext. - *

- */ - public void put(String key, float value) { - super.put(key, new Float(value)); - } - - /** - *

- * Adds the given double value to the SchedulerContext. - *

- */ - public void put(String key, double value) { - super.put(key, new Double(value)); - } - - /** - *

- * Adds the given boolean value to the SchedulerContext. - *

- */ - public void put(String key, boolean value) { - super.put(key, new Boolean(value)); - } - - /** - *

- * Adds the given char value to the SchedulerContext. - *

- */ - public void put(String key, char value) { - super.put(key, new Character(value)); - } - - /** - *

- * Adds the given String value to the SchedulerContext. - *

- */ - public void put(String key, String value) { - super.put(key, value); - } - - /** - *

- * Adds the given Object value to the SchedulerContext. - *

- */ - 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 SchedulerContext. - *

- * - * @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 SchedulerContext. - *

- * - * @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 SchedulerContext. - *

- * - * @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 SchedulerContext. - *

- * - * @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 SchedulerContext. - *

- * - * @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 SchedulerContext. - *

- * - * @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 SchedulerContext. - *

- * - * @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."); - } - } - - public String[] getKeys() { - return (String[]) keySet().toArray(new String[size()]); - } - } Index: 3rdParty_sources/quartz/org/quartz/SchedulerException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/SchedulerException.java (.../SchedulerException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/SchedulerException.java (.../SchedulerException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,91 +16,27 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; -import java.io.PrintStream; -import java.io.PrintWriter; + /** - *

* Base class for exceptions thrown by the Quartz {@link Scheduler}. - *

* *

- * SchedulerException s may contain a reference to another + * SchedulerExceptions may contain a reference to another * Exception, which was the underlying cause of the SchedulerException. *

* * @author James House */ public class SchedulerException extends Exception { + + private static final long serialVersionUID = 174841398690789156L; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * - * Constants. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - public static final int ERR_UNSPECIFIED = 0; - - public static final int ERR_BAD_CONFIGURATION = 50; - - public static final int ERR_TIME_BROKER_FAILURE = 70; - - public static final int ERR_CLIENT_ERROR = 100; - - public static final int ERR_COMMUNICATION_FAILURE = 200; - - public static final int ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION = 210; - - public static final int ERR_PERSISTENCE = 400; - - public static final int ERR_PERSISTENCE_JOB_DOES_NOT_EXIST = 410; - - public static final int ERR_PERSISTENCE_CALENDAR_DOES_NOT_EXIST = 420; - - public static final int ERR_PERSISTENCE_TRIGGER_DOES_NOT_EXIST = 430; - - public static final int ERR_PERSISTENCE_CRITICAL_FAILURE = 499; - - public static final int ERR_THREAD_POOL = 500; - - public static final int ERR_THREAD_POOL_EXHAUSTED = 510; - - public static final int ERR_THREAD_POOL_CRITICAL_FAILURE = 599; - - public static final int ERR_JOB_LISTENER = 600; - - public static final int ERR_JOB_LISTENER_NOT_FOUND = 610; - - public static final int ERR_TRIGGER_LISTENER = 700; - - public static final int ERR_TRIGGER_LISTENER_NOT_FOUND = 710; - - public static final int ERR_JOB_EXECUTION_THREW_EXCEPTION = 800; - - public static final int ERR_TRIGGER_THREW_EXCEPTION = 850; - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - private Exception cause; - - private int errorCode = ERR_UNSPECIFIED; - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * * Constructors. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -114,27 +50,16 @@ super(msg); } - public SchedulerException(String msg, int errorCode) { - super(msg); - setErrorCode(errorCode); + public SchedulerException(Throwable cause) { + super(cause); } - public SchedulerException(Exception cause) { - super(cause.toString()); - this.cause = cause; + public SchedulerException(String msg, Throwable cause) { + super(msg, cause); } - public SchedulerException(String msg, Exception cause) { - super(msg); - this.cause = cause; - } - public SchedulerException(String msg, Exception cause, int errorCode) { - super(msg); - this.cause = cause; - setErrorCode(errorCode); - } - + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -156,167 +81,18 @@ * one. */ public Throwable getUnderlyingException() { - return cause; + return super.getCause(); } - /** - *

- * 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. - *

- */ - public boolean isPersistenceError() { - return (errorCode >= ERR_PERSISTENCE && errorCode <= ERR_PERSISTENCE + 99); - } - - /** - *

- * Determine if the specified error code is in the 'ERR_THREAD_POOL' - * category of errors. - *

- */ - public boolean isThreadPoolError() { - return (errorCode >= ERR_THREAD_POOL && errorCode <= ERR_THREAD_POOL + 99); - } - - /** - *

- * Determine if the specified error code is in the 'ERR_JOB_LISTENER' - * category of errors. - *

- */ - public boolean isJobListenerError() { - return (errorCode >= ERR_JOB_LISTENER && errorCode <= ERR_JOB_LISTENER + 99); - } - - /** - *

- * Determine if the specified error code is in the 'ERR_TRIGGER_LISTENER' - * category of errors. - *

- */ - public boolean isTriggerListenerError() { - return (errorCode >= ERR_TRIGGER_LISTENER && errorCode <= ERR_TRIGGER_LISTENER + 99); - } - - /** - *

- * Determine if the specified error code is in the 'ERR_CLIENT_ERROR' - * category of errors. - *

- */ - public boolean isClientError() { - return (errorCode >= ERR_CLIENT_ERROR && errorCode <= ERR_CLIENT_ERROR + 99); - } - - /** - *

- * Determine if the specified error code is in the 'ERR_CLIENT_ERROR' - * category of errors. - *

- */ - public boolean isConfigurationError() { - return (errorCode >= ERR_BAD_CONFIGURATION && errorCode <= ERR_BAD_CONFIGURATION + 49); - } - + @Override public String toString() { - if (cause == null) return super.toString(); - else - return super.toString() + " [See nested exception: " - + cause.toString() + "]"; - } - - /** - *

- * 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); - } + Throwable cause = getUnderlyingException(); + if (cause == null || cause == this) { + return super.toString(); + } else { + return super.toString() + " [See nested exception: " + cause + "]"; } } - /** - *

- * 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); - } - } - } } Index: 3rdParty_sources/quartz/org/quartz/SchedulerFactory.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/SchedulerFactory.java (.../SchedulerFactory.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/SchedulerFactory.java (.../SchedulerFactory.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,18 +16,13 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; import java.util.Collection; /** - *

* Provides a mechanism for obtaining client-usable handles to Scheduler * instances. - *

* * @see Scheduler * @see org.quartz.impl.StdSchedulerFactory @@ -52,21 +47,21 @@ * @throws SchedulerException * if there is a problem with the underlying Scheduler. */ - public Scheduler getScheduler() throws SchedulerException; + Scheduler getScheduler() throws SchedulerException; /** *

* Returns a handle to the Scheduler with the given name, if it exists. *

*/ - public Scheduler getScheduler(String schedName) throws SchedulerException; + Scheduler getScheduler(String schedName) throws SchedulerException; /** *

* Returns handles to all known Schedulers (made by any SchedulerFactory * within this jvm.). *

*/ - public Collection getAllSchedulers() throws SchedulerException; + Collection getAllSchedulers() throws SchedulerException; } Index: 3rdParty_sources/quartz/org/quartz/SchedulerListener.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/SchedulerListener.java (.../SchedulerListener.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/SchedulerListener.java (.../SchedulerListener.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,16 +16,11 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; /** - *

* The interface to be implemented by classes that want to be informed of major * {@link Scheduler} events. - *

* * @see Scheduler * @see JobListener @@ -49,82 +44,116 @@ * is scheduled. *

*/ - public void jobScheduled(Trigger trigger); + void jobScheduled(Trigger trigger); /** *

* Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} * is unscheduled. *

+ * + * @see SchedulerListener#schedulingDataCleared() */ - public void jobUnscheduled(String triggerName, String triggerGroup); + void jobUnscheduled(TriggerKey triggerKey); /** *

* Called by the {@link Scheduler} when a {@link Trigger} * has reached the condition in which it will never fire again. *

*/ - public void triggerFinalized(Trigger trigger); + void triggerFinalized(Trigger trigger); /** *

* Called by the {@link Scheduler} when a {@link Trigger} - * or group of {@link Trigger}s has been paused. + * has been paused. *

- * + */ + void triggerPaused(TriggerKey triggerKey); + + /** *

- * If a group was paused, then the triggerName parameter - * will be null. + * Called by the {@link Scheduler} when a + * group of {@link Trigger}s has been paused. *

+ * + *

If all groups were paused then triggerGroup will be null

+ * + * @param triggerGroup the paused group, or null if all were paused */ - public void triggersPaused(String triggerName, String triggerGroup); - + void triggersPaused(String triggerGroup); + /** *

* Called by the {@link Scheduler} when a {@link Trigger} - * or group of {@link Trigger}s has been un-paused. + * has been un-paused. *

- * + */ + void triggerResumed(TriggerKey triggerKey); + + /** *

- * If a group was resumed, then the triggerName parameter - * will be null. + * Called by the {@link Scheduler} when a + * group of {@link Trigger}s has been un-paused. *

*/ - public void triggersResumed(String triggerName, String triggerGroup); + void triggersResumed(String triggerGroup); /** *

* Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} - * or group of {@link org.quartz.JobDetail}s has been - * paused. + * has been added. *

- * + */ + void jobAdded(JobDetail jobDetail); + + /** *

- * 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 org.quartz.JobDetail} + * has been deleted. *

*/ - public void jobsPaused(String jobName, String jobGroup); - + void jobDeleted(JobKey jobKey); + /** *

* Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} - * or group of {@link org.quartz.JobDetail}s has been - * un-paused. + * has been paused. *

+ */ + void jobPaused(JobKey jobKey); + + /** + *

+ * Called by the {@link Scheduler} when a + * group of {@link org.quartz.JobDetail}s has been paused. + *

* + * @param jobGroup the paused group, or null if all were paused + */ + void jobsPaused(String jobGroup); + + /** *

- * 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 {@link org.quartz.JobDetail} + * has been un-paused. *

*/ - public void jobsResumed(String jobName, String jobGroup); + void jobResumed(JobKey jobKey); /** *

+ * Called by the {@link Scheduler} when a + * group of {@link org.quartz.JobDetail}s has been un-paused. + *

+ */ + void jobsResumed(String jobGroup); + + /** + *

* Called by the {@link Scheduler} when a serious error has - * occured within the scheduler - such as repeated failures in the JobStore, + * occurred within the scheduler - such as repeated failures in the JobStore, * or the inability to instantiate a Job instance when its * Trigger has fired. *

@@ -135,14 +164,51 @@ * error that was encountered. *

*/ - public void schedulerError(String msg, SchedulerException cause); + void schedulerError(String msg, SchedulerException cause); /** *

* Called by the {@link Scheduler} to inform the listener + * that it has move to standby mode. + *

+ */ + void schedulerInStandbyMode(); + + /** + *

+ * Called by the {@link Scheduler} to inform the listener + * that it has started. + *

+ */ + void schedulerStarted(); + + /** + *

+ * Called by the {@link Scheduler} to inform the listener + * that it is starting. + *

+ */ + void schedulerStarting(); + + /** + *

+ * Called by the {@link Scheduler} to inform the listener * that it has shutdown. *

*/ - public void schedulerShutdown(); + void schedulerShutdown(); + + /** + *

+ * Called by the {@link Scheduler} to inform the listener + * that it has begun the shutdown sequence. + *

+ */ + void schedulerShuttingdown(); + /** + * Called by the {@link Scheduler} to inform the listener + * that all jobs, triggers and calendars were deleted. + */ + void schedulingDataCleared(); } Index: 3rdParty_sources/quartz/org/quartz/SchedulerMetaData.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/SchedulerMetaData.java (.../SchedulerMetaData.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/SchedulerMetaData.java (.../SchedulerMetaData.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,22 +16,19 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; import java.util.Date; /** - *

* Describes the settings and capabilities of a given {@link Scheduler} * instance. - *

* * @author James House */ public class SchedulerMetaData implements java.io.Serializable { + + private static final long serialVersionUID = 4203690002633917647L; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -45,26 +42,28 @@ private String schedInst; - private Class schedClass; + private Class schedClass; private boolean isRemote; private boolean started; - private boolean paused; + private boolean isInStandbyMode; private boolean shutdown; private Date startTime; private int numJobsExec; - private Class jsClass; + private Class jsClass; private boolean jsPersistent; - private Class tpClass; + private boolean jsClustered; + private Class tpClass; + private int tpSize; private String version; @@ -78,21 +77,22 @@ */ public SchedulerMetaData(String schedName, String schedInst, - Class schedClass, boolean isRemote, boolean started, - boolean paused, boolean shutdown, Date startTime, int numJobsExec, - Class jsClass, boolean jsPersistent, Class tpClass, int tpSize, + Class schedClass, boolean isRemote, boolean started, + boolean isInStandbyMode, boolean shutdown, Date startTime, int numJobsExec, + Class jsClass, boolean jsPersistent, boolean jsClustered, Class tpClass, int tpSize, String version) { this.schedName = schedName; this.schedInst = schedInst; this.schedClass = schedClass; this.isRemote = isRemote; this.started = started; - this.paused = paused; + this.isInStandbyMode = isInStandbyMode; this.shutdown = shutdown; this.startTime = startTime; this.numJobsExec = numJobsExec; this.jsClass = jsClass; this.jsPersistent = jsPersistent; + this.jsClustered = jsClustered; this.tpClass = tpClass; this.tpSize = tpSize; this.version = version; @@ -129,7 +129,7 @@ * Returns the class-name of the Scheduler instance. *

*/ - public Class getSchedulerClass() { + public Class getSchedulerClass() { return schedClass; } @@ -140,17 +140,17 @@ * * @return null if the scheduler has not been started. */ - public Date runningSince() { + public Date getRunningSince() { return startTime; } - + /** *

* Returns the number of jobs executed since the Scheduler * started.. *

*/ - public int numJobsExecuted() { + public int getNumberOfJobsExecuted() { return numJobsExec; } @@ -171,25 +171,18 @@ * *

* Note: isStarted() may return true even if - * isPaused() returns true. + * isInStandbyMode() returns true. *

*/ public boolean isStarted() { return started; } /** - *

- * Reports whether the Scheduler is paused. - *

- * - *

- * Note: isStarted() may return true even if - * isPaused() returns true. - *

+ * Reports whether the Scheduler is in standby mode. */ - public boolean isPaused() { - return paused; + public boolean isInStandbyMode() { + return isInStandbyMode; } /** @@ -207,27 +200,37 @@ * being used by the Scheduler. *

*/ - public Class getJobStoreClass() { + public Class getJobStoreClass() { return jsClass; } - + /** *

* Returns whether or not the Scheduler'sJobStore * instance supports persistence. *

*/ - public boolean jobStoreSupportsPersistence() { + public boolean isJobStoreSupportsPersistence() { return jsPersistent; } /** *

+ * Returns whether or not the Scheduler'sJobStore + * is clustered. + *

+ */ + public boolean isJobStoreClustered() { + return jsClustered; + } + + /** + *

* Returns the class-name of the ThreadPool instance that is * being used by the Scheduler. *

*/ - public Class getThreadPoolClass() { + public Class getThreadPoolClass() { return tpClass; } @@ -255,6 +258,7 @@ * Return a simple string representation of this object. *

*/ + @Override public String toString() { try { return getSummary(); @@ -281,7 +285,7 @@ *

*/ public String getSummary() throws SchedulerException { - StringBuffer str = new StringBuffer("Quartz Scheduler (v"); + StringBuilder str = new StringBuilder("Quartz Scheduler (v"); str.append(getVersion()); str.append(") '"); @@ -293,29 +297,34 @@ str.append(" Scheduler class: '"); str.append(getSchedulerClass().getName()); str.append("'"); - if (isSchedulerRemote()) str.append(" - access via RMI."); - else + if (isSchedulerRemote()) { + str.append(" - access via RMI."); + } else { str.append(" - running locally."); + } str.append("\n"); if (!isShutdown()) { - if (runningSince() != null) { + if (getRunningSince() != null) { str.append(" Running since: "); - str.append(runningSince()); - } else - str.append("NOT STARTED."); + str.append(getRunningSince()); + } else { + str.append(" NOT STARTED."); + } str.append("\n"); - if (isPaused()) str.append(" Currently PAUSED."); - else - str.append(" Not currently paused."); + 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(getNumberOfJobsExecuted()); str.append("\n"); str.append(" Using thread pool '"); @@ -328,9 +337,16 @@ str.append(" Using job-store '"); str.append(getJobStoreClass().getName()); str.append("' - which "); - if (jobStoreSupportsPersistence()) str.append("supports persistence."); - else + if (isJobStoreSupportsPersistence()) { + str.append("supports persistence."); + } else { str.append("does not support persistence."); + } + if (isJobStoreClustered()) { + str.append(" and is clustered."); + } else { + str.append(" and is not clustered."); + } str.append("\n"); return str.toString(); Index: 3rdParty_sources/quartz/org/quartz/SimpleScheduleBuilder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/SimpleScheduleBuilder.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/SimpleScheduleBuilder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,429 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import org.quartz.impl.triggers.SimpleTriggerImpl; +import org.quartz.spi.MutableTrigger; + +/** + * SimpleScheduleBuilder is a {@link ScheduleBuilder} + * that defines strict/literal interval-based schedules for + * Triggers. + * + *

Quartz provides a builder-style API for constructing scheduling-related + * entities via a Domain-Specific Language (DSL). The DSL can best be + * utilized through the usage of static imports of the methods on the classes + * TriggerBuilder, JobBuilder, + * DateBuilder, JobKey, TriggerKey + * and the various ScheduleBuilder implementations.

+ * + *

Client code can then use the DSL to write code such as this:

+ *
+ *         JobDetail job = newJob(MyJob.class)
+ *             .withIdentity("myJob")
+ *             .build();
+ *             
+ *         Trigger trigger = newTrigger() 
+ *             .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+ *             .withSchedule(simpleSchedule()
+ *                 .withIntervalInHours(1)
+ *                 .repeatForever())
+ *             .startAt(futureDate(10, MINUTES))
+ *             .build();
+ *         
+ *         scheduler.scheduleJob(job, trigger);
+ * 
+ *
+ * @see SimpleTrigger
+ * @see CalendarIntervalScheduleBuilder
+ * @see CronScheduleBuilder
+ * @see ScheduleBuilder
+ * @see TriggerBuilder
+ */
+public class SimpleScheduleBuilder extends ScheduleBuilder {
+
+    private long interval = 0;
+    private int repeatCount = 0;
+    private int misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_SMART_POLICY;
+    
+    protected SimpleScheduleBuilder() {
+    }
+    
+    /**
+     * Create a SimpleScheduleBuilder.
+     * 
+     * @return the new SimpleScheduleBuilder
+     */
+    public static SimpleScheduleBuilder simpleSchedule() {
+        return new SimpleScheduleBuilder();
+    }
+    
+    /**
+     * Create a SimpleScheduleBuilder set to repeat forever with a 1 minute interval.
+     * 
+     * @return the new SimpleScheduleBuilder
+     */
+    public static SimpleScheduleBuilder repeatMinutelyForever() {
+
+        return simpleSchedule()
+            .withIntervalInMinutes(1)
+            .repeatForever();
+    }
+
+    /**
+     * Create a SimpleScheduleBuilder set to repeat forever with an interval
+     * of the given number of minutes.
+     * 
+     * @return the new SimpleScheduleBuilder
+     */
+    public static SimpleScheduleBuilder repeatMinutelyForever(int minutes) {
+
+        return simpleSchedule()
+            .withIntervalInMinutes(minutes)
+            .repeatForever();
+    }
+
+    /**
+     * Create a SimpleScheduleBuilder set to repeat forever with a 1 second interval.
+     * 
+     * @return the new SimpleScheduleBuilder
+     */
+    public static SimpleScheduleBuilder repeatSecondlyForever() {
+
+        return simpleSchedule()
+            .withIntervalInSeconds(1)
+            .repeatForever();
+    }
+
+    /**
+     * Create a SimpleScheduleBuilder set to repeat forever with an interval
+     * of the given number of seconds.
+     * 
+     * @return the new SimpleScheduleBuilder
+     */
+    public static SimpleScheduleBuilder repeatSecondlyForever(int seconds) {
+
+        return simpleSchedule()
+            .withIntervalInSeconds(seconds)
+            .repeatForever();
+    }
+    
+    /**
+     * Create a SimpleScheduleBuilder set to repeat forever with a 1 hour interval.
+     * 
+     * @return the new SimpleScheduleBuilder
+     */
+    public static SimpleScheduleBuilder repeatHourlyForever() {
+
+        return simpleSchedule()
+            .withIntervalInHours(1)
+            .repeatForever();
+    }
+
+    /**
+     * Create a SimpleScheduleBuilder set to repeat forever with an interval
+     * of the given number of hours.
+     * 
+     * @return the new SimpleScheduleBuilder
+     */
+    public static SimpleScheduleBuilder repeatHourlyForever(int hours) {
+
+        return simpleSchedule()
+            .withIntervalInHours(hours)
+            .repeatForever();
+    }
+
+    /**
+     * Create a SimpleScheduleBuilder set to repeat the given number
+     * of times - 1  with a 1 minute interval.
+     * 
+     * 

Note: Total count = 1 (at start time) + repeat count

+ * + * @return the new SimpleScheduleBuilder + */ + public static SimpleScheduleBuilder repeatMinutelyForTotalCount(int count) { + if(count < 1) + throw new IllegalArgumentException("Total count of firings must be at least one! Given count: " + count); + + return simpleSchedule() + .withIntervalInMinutes(1) + .withRepeatCount(count - 1); + } + + /** + * Create a SimpleScheduleBuilder set to repeat the given number + * of times - 1 with an interval of the given number of minutes. + * + *

Note: Total count = 1 (at start time) + repeat count

+ * + * @return the new SimpleScheduleBuilder + */ + public static SimpleScheduleBuilder repeatMinutelyForTotalCount(int count, int minutes) { + if(count < 1) + throw new IllegalArgumentException("Total count of firings must be at least one! Given count: " + count); + + return simpleSchedule() + .withIntervalInMinutes(minutes) + .withRepeatCount(count - 1); + } + + /** + * Create a SimpleScheduleBuilder set to repeat the given number + * of times - 1 with a 1 second interval. + * + *

Note: Total count = 1 (at start time) + repeat count

+ * + * @return the new SimpleScheduleBuilder + */ + public static SimpleScheduleBuilder repeatSecondlyForTotalCount(int count) { + if(count < 1) + throw new IllegalArgumentException("Total count of firings must be at least one! Given count: " + count); + + return simpleSchedule() + .withIntervalInSeconds(1) + .withRepeatCount(count - 1); + } + + /** + * Create a SimpleScheduleBuilder set to repeat the given number + * of times - 1 with an interval of the given number of seconds. + * + *

Note: Total count = 1 (at start time) + repeat count

+ * + * @return the new SimpleScheduleBuilder + */ + public static SimpleScheduleBuilder repeatSecondlyForTotalCount(int count, int seconds) { + if(count < 1) + throw new IllegalArgumentException("Total count of firings must be at least one! Given count: " + count); + + return simpleSchedule() + .withIntervalInSeconds(seconds) + .withRepeatCount(count - 1); + } + + /** + * Create a SimpleScheduleBuilder set to repeat the given number + * of times - 1 with a 1 hour interval. + * + *

Note: Total count = 1 (at start time) + repeat count

+ * + * @return the new SimpleScheduleBuilder + */ + public static SimpleScheduleBuilder repeatHourlyForTotalCount(int count) { + if(count < 1) + throw new IllegalArgumentException("Total count of firings must be at least one! Given count: " + count); + + return simpleSchedule() + .withIntervalInHours(1) + .withRepeatCount(count - 1); + } + + /** + * Create a SimpleScheduleBuilder set to repeat the given number + * of times - 1 with an interval of the given number of hours. + * + *

Note: Total count = 1 (at start time) + repeat count

+ * + * @return the new SimpleScheduleBuilder + */ + public static SimpleScheduleBuilder repeatHourlyForTotalCount(int count, int hours) { + if(count < 1) + throw new IllegalArgumentException("Total count of firings must be at least one! Given count: " + count); + + return simpleSchedule() + .withIntervalInHours(hours) + .withRepeatCount(count - 1); + } + + /** + * Build the actual Trigger -- NOT intended to be invoked by end users, + * but will rather be invoked by a TriggerBuilder which this + * ScheduleBuilder is given to. + * + * @see TriggerBuilder#withSchedule(ScheduleBuilder) + */ + @Override + public MutableTrigger build() { + + SimpleTriggerImpl st = new SimpleTriggerImpl(); + st.setRepeatInterval(interval); + st.setRepeatCount(repeatCount); + st.setMisfireInstruction(misfireInstruction); + + return st; + } + + /** + * Specify a repeat interval in milliseconds. + * + * @param intervalInMillis the number of seconds at which the trigger should repeat. + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#getRepeatInterval() + * @see #withRepeatCount(int) + */ + public SimpleScheduleBuilder withIntervalInMilliseconds(long intervalInMillis) { + this.interval = intervalInMillis; + return this; + } + + /** + * Specify a repeat interval in seconds - which will then be multiplied + * by 1000 to produce milliseconds. + * + * @param intervalInSeconds the number of seconds at which the trigger should repeat. + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#getRepeatInterval() + * @see #withRepeatCount(int) + */ + public SimpleScheduleBuilder withIntervalInSeconds(int intervalInSeconds) { + this.interval = intervalInSeconds * 1000L; + return this; + } + + /** + * Specify a repeat interval in minutes - which will then be multiplied + * by 60 * 1000 to produce milliseconds. + * + * @param intervalInMinutes the number of seconds at which the trigger should repeat. + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#getRepeatInterval() + * @see #withRepeatCount(int) + */ + public SimpleScheduleBuilder withIntervalInMinutes(int intervalInMinutes) { + this.interval = intervalInMinutes * DateBuilder.MILLISECONDS_IN_MINUTE; + return this; + } + + /** + * Specify a repeat interval in minutes - which will then be multiplied + * by 60 * 60 * 1000 to produce milliseconds. + * + * @param intervalInHours the number of seconds at which the trigger should repeat. + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#getRepeatInterval() + * @see #withRepeatCount(int) + */ + public SimpleScheduleBuilder withIntervalInHours(int intervalInHours) { + this.interval = intervalInHours * DateBuilder.MILLISECONDS_IN_HOUR; + return this; + } + + /** + * Specify a the number of time the trigger will repeat - total number of + * firings will be this number + 1. + * + * @param triggerRepeatCount the number of seconds at which the trigger should repeat. + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#getRepeatCount() + * @see #repeatForever() + */ + public SimpleScheduleBuilder withRepeatCount(int triggerRepeatCount) { + this.repeatCount = triggerRepeatCount; + return this; + } + + /** + * Specify that the trigger will repeat indefinitely. + * + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#getRepeatCount() + * @see SimpleTrigger#REPEAT_INDEFINITELY + * @see #withIntervalInMilliseconds(long) + * @see #withIntervalInSeconds(int) + * @see #withIntervalInMinutes(int) + * @see #withIntervalInHours(int) + */ + public SimpleScheduleBuilder repeatForever() { + this.repeatCount = SimpleTrigger.REPEAT_INDEFINITELY; + return this; + } + + /** + * If the Trigger misfires, use the + * {@link Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY} instruction. + * + * @return the updated CronScheduleBuilder + * @see Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY + */ + public SimpleScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() { + misfireInstruction = Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY; + return this; + } + + /** + * If the Trigger misfires, use the + * {@link SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW} instruction. + * + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW + */ + + public SimpleScheduleBuilder withMisfireHandlingInstructionFireNow() { + misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW; + return this; + } + + /** + * If the Trigger misfires, use the + * {@link SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT} instruction. + * + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT + */ + public SimpleScheduleBuilder withMisfireHandlingInstructionNextWithExistingCount() { + misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT; + return this; + } + + /** + * If the Trigger misfires, use the + * {@link SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT} instruction. + * + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT + */ + public SimpleScheduleBuilder withMisfireHandlingInstructionNextWithRemainingCount() { + misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT; + return this; + } + + /** + * If the Trigger misfires, use the + * {@link SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT} instruction. + * + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT + */ + public SimpleScheduleBuilder withMisfireHandlingInstructionNowWithExistingCount() { + misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT; + return this; + } + + /** + * If the Trigger misfires, use the + * {@link SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT} instruction. + * + * @return the updated SimpleScheduleBuilder + * @see SimpleTrigger#MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT + */ + public SimpleScheduleBuilder withMisfireHandlingInstructionNowWithRemainingCount() { + misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT; + return this; + } + +} Index: 3rdParty_sources/quartz/org/quartz/SimpleTrigger.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/SimpleTrigger.java (.../SimpleTrigger.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/SimpleTrigger.java (.../SimpleTrigger.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,5 @@ - -/* - * Copyright 2004-2005 OpenSymphony +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. * * 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 @@ -16,37 +15,22 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; -import java.util.Date; - - /** - *

- * A concrete {@link Trigger} that is used to fire a {@link org.quartz.JobDetail} + * A {@link Trigger} that is used to fire a Job * at a given moment in time, and optionally repeated at a specified interval. - *

* - * @see Trigger - * @see CronTrigger - * @see TriggerUtils + * @see TriggerBuilder + * @see SimpleScheduleBuilder * * @author James House * @author contributions by Lieven Govaerts of Ebitec Nv, Belgium. */ -public class SimpleTrigger extends Trigger { +public interface SimpleTrigger extends Trigger { - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Constants. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - + public static final long serialVersionUID = -3735980074222850397L; + /** *

* Instructs the {@link Scheduler} that upon a mis-fire @@ -62,13 +46,15 @@ *

*/ public static final int MISFIRE_INSTRUCTION_FIRE_NOW = 1; - + /** *

* 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. + * 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. *

* *

@@ -77,29 +63,27 @@ * 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. - *

*/ 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. + * 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 (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). + * 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). *

* *

@@ -109,7 +93,7 @@ *

*/ public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT = 3; - + /** *

* Instructs the {@link Scheduler} that upon a mis-fire @@ -125,7 +109,7 @@ *

*/ public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT = 4; - + /** *

* Instructs the {@link Scheduler} that upon a mis-fire @@ -136,705 +120,45 @@ *

* *

- * NOTE: Use of this instruction causes the trigger to 'forget' - * the 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). - *

- * - *

* NOTE/WARNING: This instruction could cause the Trigger - * to go directly to the 'COMPLETE' state if all fire-times where missed. + * to go directly to the 'COMPLETE' state if the end-time of the trigger + * has arrived. *

*/ public static final int MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT = 5; - + /** *

* 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 int REPEAT_INDEFINITELY = -1; + public static final int REPEAT_INDEFINITELY = -1; - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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. - *

- */ - public SimpleTrigger() { - super(); - } - - /** - *

- * Create a SimpleTrigger that will occur immediately, and - * not repeat. - *

- */ - public SimpleTrigger(String name, String group) { - this(name, group, new Date(), null, 0, 0); - } - - /** - *

- * Create a SimpleTrigger that will occur immediately, and - * repeat at the the given interval the given number of times. - *

- */ - public SimpleTrigger(String name, String group, int repeatCount, - long repeatInterval) { - this(name, group, new Date(), null, repeatCount, repeatInterval); - } - - /** - *

- * Create a SimpleTrigger that will occur at the given time, - * and not repeat. - *

- */ - public SimpleTrigger(String name, String group, Date startTime) { - this(name, group, startTime, null, 0, 0); - } - - /** - *

- * 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. - *

- * - * @param startTime - * A 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, 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. - *

- * - * @param startTime - * A 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. - *

- */ - public Date getStartTime() { - return startTime; - } - - /** - *

- * Set the time at which the SimpleTrigger should occur. - *

- * - * @exception IllegalArgumentException - * if startTime is 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. - *

- * - * @see #getFinalFireTime() - */ - public Date getEndTime() { - return endTime; - } - - /** - *

- * Set the time at which the SimpleTrigger should quit - * repeating (and be automatically deleted). - *

- * - * @exception IllegalArgumentException - * if endTime is before start time. - */ - public void setEndTime(Date endTime) { - Date sTime = getStartTime(); - if (sTime != null && endTime != null && sTime.after(endTime)) - throw new IllegalArgumentException( - "End time cannot be before start time"); - - this.endTime = endTime; - } - - /** - *

* Get the the number of times the SimpleTrigger should * repeat, after which it will be automatically deleted. *

* * @see #REPEAT_INDEFINITELY */ - public int getRepeatCount() { - return repeatCount; - } + public int getRepeatCount(); /** *

- * 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. *

- * - * @see #REPEAT_INDEFINITELY - * @exception IllegalArgumentException - * if repeatCount is < 0 */ - public void setRepeatCount(int repeatCount) { - if (repeatCount < 0 && repeatCount != REPEAT_INDEFINITELY) - throw new IllegalArgumentException( - "Repeat count must be >= 0, use the " - + "constant REPEAT_INDEFINITELY for infinite."); - - this.repeatCount = repeatCount; - } - + public long getRepeatInterval(); + /** *

- * Get the the time interval (in milliseconds) at which the SimpleTrigger - * should repeat. + * Get the number of times the SimpleTrigger has already fired. *

*/ - public long getRepeatInterval() { - return repeatInterval; - } + public int getTimesTriggered(); - /** - *

- * Set the the time interval (in milliseconds) at which the SimpleTrigger - * should repeat. - *

- * - * @exception IllegalArgumentException - * if repeatInterval is <= 0 - */ - public void setRepeatInterval(long repeatInterval) { - if (repeatInterval < 0) - throw new IllegalArgumentException( - "Repeat interval must be >= 0"); - - this.repeatInterval = repeatInterval; - } - - /** - *

- * Get the number of times the SimpleTrigger has already - * fired. - *

- */ - public int getTimesTriggered() { - return timesTriggered; - } - - /** - *

- * Set the number of times the SimpleTrigger has already - * fired. - *

- */ - public void setTimesTriggered(int timesTriggered) { - this.timesTriggered = timesTriggered; - } - - protected boolean validateMisfireInstruction(int misfireInstruction) { - if (misfireInstruction < MISFIRE_INSTRUCTION_SMART_POLICY) - return false; - - if (misfireInstruction > MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) - return false; - - return true; - } - - /** - *

- * 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:
- *

    - *
  • If the Repeat Count is 0, then the instruction will - * be interpreted as MISFIRE_INSTRUCTION_FIRE_NOW.
  • - *
  • If the Repeat Count is 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. - *
  • - *
  • If the Repeat Count is > 0, then the instruction - * will be interpreted as MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT. - *
  • - *
- *

- */ - public void updateAfterMisfire(Calendar cal) { - int instr = getMisfireInstruction(); - if (instr == Trigger.MISFIRE_INSTRUCTION_SMART_POLICY) { - if (getRepeatCount() == 0) instr = MISFIRE_INSTRUCTION_FIRE_NOW; - else if (getRepeatCount() == REPEAT_INDEFINITELY) instr = MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT; - else - // if (getRepeatCount() > 0) - instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT; - } else if (instr == MISFIRE_INSTRUCTION_FIRE_NOW - && getRepeatCount() != 0) - instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT; - - if (instr == MISFIRE_INSTRUCTION_FIRE_NOW) { - setNextFireTime(new Date()); - } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) { - Date newFireTime = getFireTimeAfter(new Date()); - while (newFireTime != null && cal != null - && !cal.isTimeIncluded(newFireTime.getTime())) { - newFireTime = getFireTimeAfter(newFireTime); - } - setNextFireTime(newFireTime); - } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT) { - Date newFireTime = getFireTimeAfter(new Date()); - while (newFireTime != null && cal != null - && !cal.isTimeIncluded(newFireTime.getTime())) { - newFireTime = getFireTimeAfter(newFireTime); - } - if (newFireTime != null) { - int timesMissed = computeNumTimesFiredBetween(nextFireTime, - newFireTime); - setTimesTriggered(getTimesTriggered() + timesMissed); - } - - setNextFireTime(newFireTime); - } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT) { - Date newFireTime = new Date(); - if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) { - setRepeatCount(getRepeatCount() - getTimesTriggered()); - setTimesTriggered(0); - } - setStartTime(newFireTime); - setNextFireTime(newFireTime); - } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT) { - Date newFireTime = new Date(); - - int timesMissed = computeNumTimesFiredBetween(nextFireTime, - newFireTime); - - if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) { - int remainingCount = getRepeatCount() - - (getTimesTriggered() + timesMissed); - if (remainingCount <= 0) { - remainingCount = 0; - } - setRepeatCount(remainingCount); - setTimesTriggered(0); - } - - if(getEndTime() != null && getEndTime().before(newFireTime)) - setEndTime(new Date(newFireTime.getTime() + 50)); - - setStartTime(newFireTime); - setNextFireTime(newFireTime); - } - - } - - /** - *

- * 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). - *

- * - * @see #executionComplete(JobExecutionContext, JobExecutionException) - */ - public void triggered(Calendar calendar) { - timesTriggered++; - previousFireTime = nextFireTime; - nextFireTime = getFireTimeAfter(nextFireTime); - - while (nextFireTime != null && calendar != null - && !calendar.isTimeIncluded(nextFireTime.getTime())) { - nextFireTime = getFireTimeAfter(nextFireTime); - } - } - - /** - * - * @see org.quartz.Trigger#updateWithNewCalendar(org.quartz.Calendar, long) - */ - public void updateWithNewCalendar(Calendar calendar, long misfireThreshold) - { - nextFireTime = getFireTimeAfter(previousFireTime); - - Date now = new Date(); - do { - while (nextFireTime != null && calendar != null - && !calendar.isTimeIncluded(nextFireTime.getTime())) { - nextFireTime = getFireTimeAfter(nextFireTime); - } - - if(nextFireTime != null && nextFireTime.before(now)) { - long diff = now.getTime() - nextFireTime.getTime(); - if(diff >= misfireThreshold) { - nextFireTime = getFireTimeAfter(nextFireTime); - continue; - } - } - }while(false); - } - - /** - *

- * 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. - *

- * - * @return the first time at which the 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); - } - - return nextFireTime; - } - - /** - *

- * Called after the {@link Scheduler} has executed the - * {@link org.quartz.JobDetail} associated with the Trigger - * in order to get the final instruction code from the trigger. - *

- * - * @param context - * is the 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 SimpleTrigger will - * fire. If the trigger will not fire again, null will be - * returned. The value returned is not guaranteed to be valid until after - * the Trigger has been added to the scheduler. - *

- */ - public Date getNextFireTime() { - return nextFireTime; - } - - /** - *

- * Returns the previous time at which the SimpleTrigger will - * fire. 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. - *

- */ - public Date getFireTimeAfter(Date afterTime) { - if (complete) return null; - - if ((timesTriggered > repeatCount) - && (repeatCount != REPEAT_INDEFINITELY)) return null; - - if (afterTime == null) afterTime = new Date(); - - if (repeatCount == 0 && afterTime.compareTo(getStartTime()) >= 0) - return null; - - long startMillis = getStartTime().getTime(); - long afterMillis = afterTime.getTime(); - long endMillis = (getEndTime() == null) ? Long.MAX_VALUE : getEndTime() - .getTime(); - - if (endMillis <= afterMillis) return null; - - if (startMillis < afterMillis && repeatCount == 0) return null; - - if (afterMillis < startMillis) return new Date(startMillis); - - long numberoftimesexecutedplusone = ((afterMillis - startMillis) / repeatInterval) + 1; - - if ((numberoftimesexecutedplusone > repeatCount) - && (repeatCount != REPEAT_INDEFINITELY)) return null; - - Date time = new Date((numberoftimesexecutedplusone * repeatInterval) - + startMillis); - - if (endMillis <= time.getTime()) return null; - - return time; - } - - /** - *

- * 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. - *

- */ - public Date getFireTimeBefore(Date end) { - if (end.getTime() < getStartTime().getTime()) return null; - - int numFires = computeNumTimesFiredBetween(getStartTime(), end); - - return new Date(getStartTime().getTime() + (numFires * repeatInterval)); - } - - public int computeNumTimesFiredBetween(Date start, Date end) { - long time = end.getTime() - start.getTime(); - - return (int) (time / repeatInterval); - } - - /** - *

- * 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 && getEndTime() == null) - return null; - - if (repeatCount == REPEAT_INDEFINITELY && getEndTime() == null) return null; - else if (repeatCount == REPEAT_INDEFINITELY) - return 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. - *

- */ - public boolean mayFireAgain() { - return (getNextFireTime() != null); - } - - /** - *

- * 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); - } - - 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)); - } - } - + TriggerBuilder getTriggerBuilder(); } Index: 3rdParty_sources/quartz/org/quartz/StatefulJob.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/StatefulJob.java (.../StatefulJob.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/StatefulJob.java (.../StatefulJob.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,16 +16,11 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; /** - *

* A marker interface for {@link org.quartz.JobDetail} s that * wish to have their state maintained between executions. - *

* *

* StatefulJob instances follow slightly different rules from @@ -37,13 +32,21 @@ * the execute(xx) method will be delayed. *

* + * @see DisallowConcurrentExecution + * @see PersistJobDataAfterExecution + * * @see Job * @see JobDetail * @see JobDataMap * @see Scheduler * + * + * @deprecated use DisallowConcurrentExecution and/or PersistJobDataAfterExecution annotations instead. + * * @author James House */ +@PersistJobDataAfterExecution +@DisallowConcurrentExecution public interface StatefulJob extends Job { /* Index: 3rdParty_sources/quartz/org/quartz/TimeOfDay.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/TimeOfDay.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/TimeOfDay.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,243 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +/** + * Represents a time in hour, minute and second of any given day. + * + *

The hour is in 24-hour convention, meaning values are from 0 to 23.

+ * + * @see DailyTimeIntervalScheduleBuilder + * + * @since 2.0.3 + * + * @author James House + * @author Zemian Deng + */ +public class TimeOfDay implements Serializable { + + private static final long serialVersionUID = 2964774315889061771L; + + private final int hour; + private final int minute; + private final int second; + + /** + * Create a TimeOfDay instance for the given hour, minute and second. + * + * @param hour The hour of day, between 0 and 23. + * @param minute The minute of the hour, between 0 and 59. + * @param second The second of the minute, between 0 and 59. + * @throws IllegalArgumentException if one or more of the input values is out of their valid range. + */ + public TimeOfDay(int hour, int minute, int second) { + this.hour = hour; + this.minute = minute; + this.second = second; + validate(); + } + + /** + * Create a TimeOfDay instance for the given hour and minute (at the zero second of the minute). + * + * @param hour The hour of day, between 0 and 23. + * @param minute The minute of the hour, between 0 and 59. + * @throws IllegalArgumentException if one or more of the input values is out of their valid range. + */ + public TimeOfDay(int hour, int minute) { + this.hour = hour; + this.minute = minute; + this.second = 0; + validate(); + } + + private void validate() { + if(hour < 0 || hour > 23) + throw new IllegalArgumentException("Hour must be from 0 to 23"); + if(minute < 0 || minute > 59) + throw new IllegalArgumentException("Minute must be from 0 to 59"); + if(second < 0 || second > 59) + throw new IllegalArgumentException("Second must be from 0 to 59"); + } + + /** + * Create a TimeOfDay instance for the given hour, minute and second. + * + * @param hour The hour of day, between 0 and 23. + * @param minute The minute of the hour, between 0 and 59. + * @param second The second of the minute, between 0 and 59. + * @throws IllegalArgumentException if one or more of the input values is out of their valid range. + */ + public static TimeOfDay hourMinuteAndSecondOfDay(int hour, int minute, int second) { + return new TimeOfDay(hour, minute, second); + } + + /** + * Create a TimeOfDay instance for the given hour and minute (at the zero second of the minute). + * + * @param hour The hour of day, between 0 and 23. + * @param minute The minute of the hour, between 0 and 59. + * @throws IllegalArgumentException if one or more of the input values is out of their valid range. + */ + public static TimeOfDay hourAndMinuteOfDay(int hour, int minute) { + return new TimeOfDay(hour, minute); + } + + /** + * The hour of the day (between 0 and 23). + * + * @return The hour of the day (between 0 and 23). + */ + public int getHour() { + return hour; + } + + /** + * The minute of the hour. + * + * @return The minute of the hour (between 0 and 59). + */ + public int getMinute() { + return minute; + } + + /** + * The second of the minute. + * + * @return The second of the minute (between 0 and 59). + */ + public int getSecond() { + return second; + } + + /** + * Determine with this time of day is before the given time of day. + * + * @return true this time of day is before the given time of day. + */ + public boolean before(TimeOfDay timeOfDay) { + + if(timeOfDay.hour > hour) + return true; + if(timeOfDay.hour < hour) + return false; + + if(timeOfDay.minute > minute) + return true; + if(timeOfDay.minute < minute) + return false; + + if(timeOfDay.second > second) + return true; + if(timeOfDay.second < second) + return false; + + return false; // must be equal... + } + + @Override + public boolean equals(Object obj) { + if(!(obj instanceof TimeOfDay)) + return false; + + TimeOfDay other = (TimeOfDay)obj; + + return (other.hour == hour && other.minute == minute && other.second == second); + } + + @Override + public int hashCode() { + return (hour + 1) ^ (minute + 1) ^ (second + 1); + } + + /** Return a date with time of day reset to this object values. The millisecond value will be zero. */ + public Date getTimeOfDayForDate(Date dateTime) { + if (dateTime == null) + return null; + Calendar cal = Calendar.getInstance(); + cal.setTime(dateTime); + cal.set(Calendar.HOUR_OF_DAY, hour); + cal.set(Calendar.MINUTE, minute); + cal.set(Calendar.SECOND, second); + cal.clear(Calendar.MILLISECOND); + return cal.getTime(); + } + + /** + * Create a TimeOfDay from the given date, in the system default TimeZone. + * + * @param dateTime The java.util.Date from which to extract Hour, Minute and Second. + */ + public static TimeOfDay hourAndMinuteAndSecondFromDate(Date dateTime) { + return hourAndMinuteAndSecondFromDate(dateTime, null); + } + + /** + * Create a TimeOfDay from the given date, in the given TimeZone. + * + * @param dateTime The java.util.Date from which to extract Hour, Minute and Second. + * @param tz The TimeZone from which relate Hour, Minute and Second for the given date. If null, system default + * TimeZone will be used. + */ + public static TimeOfDay hourAndMinuteAndSecondFromDate(Date dateTime, TimeZone tz) { + if (dateTime == null) + return null; + Calendar cal = Calendar.getInstance(); + cal.setTime(dateTime); + if(tz != null) + cal.setTimeZone(tz); + + return new TimeOfDay(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND)); + } + + /** + * Create a TimeOfDay from the given date (at the zero-second), in the system default TimeZone. + * + * @param dateTime The java.util.Date from which to extract Hour and Minute. + */ + public static TimeOfDay hourAndMinuteFromDate(Date dateTime) { + return hourAndMinuteFromDate(dateTime, null); + } + + /** + * Create a TimeOfDay from the given date (at the zero-second), in the system default TimeZone. + * + * @param dateTime The java.util.Date from which to extract Hour and Minute. + * @param tz The TimeZone from which relate Hour and Minute for the given date. If null, system default + * TimeZone will be used. + */ + public static TimeOfDay hourAndMinuteFromDate(Date dateTime, TimeZone tz) { + if (dateTime == null) + return null; + Calendar cal = Calendar.getInstance(); + cal.setTime(dateTime); + if(tz != null) + cal.setTimeZone(tz); + + return new TimeOfDay(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); + } + + @Override + public String toString() { + return "TimeOfDay[" + hour + ":" + minute + ":" + second + "]"; + } +} Index: 3rdParty_sources/quartz/org/quartz/Trigger.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/Trigger.java (.../Trigger.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/Trigger.java (.../Trigger.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,126 +16,85 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; +import java.io.Serializable; +import java.util.Comparator; import java.util.Date; -import java.util.LinkedList; + /** - *

- * The base abstract class to be extended by all Triggers. - *

+ * The base interface with properties common to all Triggers - + * use {@link TriggerBuilder} to instantiate an actual Trigger. * *

- * Triggers s have a name and group associated with them, which + * Triggerss have a {@link TriggerKey} associated with them, which * should uniquely identify them within a single {@link Scheduler}. *

* *

- * Triggers are the 'mechanism' by which Job s - * are scheduled. Many Trigger s can point to the same Job, + * Triggers are the 'mechanism' by which Jobs + * are scheduled. Many Triggers can point to the same Job, * but a single Trigger can only point to one Job. *

* *

* Triggers can 'send' parameters/data to Jobs by placing contents * into the JobDataMap on the Trigger. *

- * - * @see SimpleTrigger - * @see CronTrigger - * @see NthIncludedDayTrigger - * @see TriggerUtils + * + * @see TriggerBuilder * @see JobDataMap * @see JobExecutionContext + * @see TriggerUtils + * @see SimpleTrigger + * @see CronTrigger + * @see CalendarIntervalTrigger * * @author James House - * @author Sharada Jambula */ -public abstract class Trigger implements java.io.Serializable, Cloneable, - Comparable { +public interface Trigger extends Serializable, Cloneable, Comparable { - private static final long serialVersionUID = -3904243490805975570L; + public static final long serialVersionUID = -3904243490805975570L; - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + public enum TriggerState { NONE, NORMAL, PAUSED, COMPLETE, ERROR, BLOCKED } + + /** + *

NOOP Instructs the {@link Scheduler} that the + * {@link Trigger} has no further instructions.

* - * Constants. + *

RE_EXECUTE_JOB Instructs the {@link Scheduler} that the + * {@link Trigger} wants the {@link 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 + * ability to 'see' anything placed in the context by its last execution).

* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + *

SET_TRIGGER_COMPLETE Instructs the {@link Scheduler} that the + * {@link Trigger} should be put in the COMPLETE state.

+ * + *

DELETE_TRIGGER Instructs the {@link Scheduler} that the + * {@link Trigger} wants itself deleted.

+ * + *

SET_ALL_JOB_TRIGGERS_COMPLETE Instructs the {@link Scheduler} + * that all Triggers referencing the same {@link org.quartz.JobDetail} + * as this one should be put in the COMPLETE state.

+ * + *

SET_TRIGGER_ERROR Instructs the {@link Scheduler} that all + * Triggers referencing the same {@link org.quartz.JobDetail} as + * this one should be put in the ERROR state.

+ * + *

SET_ALL_JOB_TRIGGERS_ERROR Instructs the {@link Scheduler} that + * the Trigger should be put in the ERROR state.

*/ + public enum CompletedExecutionInstruction { NOOP, RE_EXECUTE_JOB, SET_TRIGGER_COMPLETE, DELETE_TRIGGER, + SET_ALL_JOB_TRIGGERS_COMPLETE, SET_TRIGGER_ERROR, SET_ALL_JOB_TRIGGERS_ERROR } /** - *

- * Instructs the {@link Scheduler} that the {@link Trigger} - * has no further instructions. - *

- */ - public static final int INSTRUCTION_NOOP = 0; - - /** - *

- * Instructs the {@link Scheduler} that the {@link Trigger} - * wants the {@link 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). - *

- */ - public static final int INSTRUCTION_RE_EXECUTE_JOB = 1; - - /** - *

- * Instructs the {@link Scheduler} that the {@link Trigger} - * should be put in the COMPLETE state. - *

- */ - public static final int INSTRUCTION_SET_TRIGGER_COMPLETE = 2; - - /** - *

- * Instructs the {@link Scheduler} that the {@link Trigger} - * wants itself deleted. - *

- */ - public static final int INSTRUCTION_DELETE_TRIGGER = 3; - - /** - *

- * Instructs the {@link Scheduler} that all Trigger - * s referencing the same {@link org.quartz.JobDetail} as - * this one should be put in the COMPLETE state. - *

- */ - public static final int INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE = 4; - - /** - *

- * Instructs the {@link Scheduler} that all Trigger - * s referencing the same {@link org.quartz.JobDetail} as - * this one should be put in the ERROR state. - *

- */ - public static final int INSTRUCTION_SET_TRIGGER_ERROR = 5; - - /** - *

- * Instructs the {@link Scheduler} that the Trigger - * should be put in the ERROR state. - *

- */ - public static final int INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR = 6; - - /** - *

* 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. - *

+ * on the Trigger to determine the mis-fire instruction, + * which logic will be trigger-implementation-dependent. * *

* In order to see if this instruction fits your needs, you should look at @@ -144,788 +103,227 @@ *

*/ public static final int MISFIRE_INSTRUCTION_SMART_POLICY = 0; - + /** - *

- * Indicates that the Trigger is in the "normal" state. - *

- */ - public final static int STATE_NORMAL = 0; - - /** - *

- * Indicates that the Trigger is in the "paused" state. - *

- */ - public final static int STATE_PAUSED = 1; - - /** - *

- * Indicates that the Trigger is in the "complete" state. - *

+ * Instructs the {@link Scheduler} that the + * Trigger will never be evaluated for a misfire situation, + * and that the scheduler will simply try to fire it as soon as it can, + * and then update the Trigger as if it had fired at the proper time. * - *

- * "Complete" indicates that the trigger has not remaining fire-times in - * its schedule. - *

+ *

NOTE: if a trigger uses this instruction, and it has missed + * several of its scheduled firings, then several rapid firings may occur + * as the trigger attempt to catch back up to where it would have been. + * For example, a SimpleTrigger that fires every 15 seconds which has + * misfired for 5 minutes will fire 20 times once it gets the chance to + * fire.

*/ - public final static int STATE_COMPLETE = 2; - + public static final int MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = -1; + /** - *

- * 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. - *

+ * The default value for priority. */ - public final static int STATE_ERROR = 3; + public static final int DEFAULT_PRIORITY = 5; + public TriggerKey getKey(); - /** - *

- * 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. - *

- * - * @see StatefulJob - */ - public final static int STATE_BLOCKED = 4; - - /** - *

- * Indicates that the Trigger does not exist. - *

- */ - public final static int STATE_NONE = -1; - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - private String name; - - private String group = Scheduler.DEFAULT_GROUP; - - private String jobName; - - private String jobGroup = Scheduler.DEFAULT_GROUP; - - private String description; + public JobKey getJobKey(); - private JobDataMap jobDataMap; - - private boolean volatility = false; - - private String calendarName = null; - - private String fireInstanceId = null; - - private int misfireInstruction = MISFIRE_INSTRUCTION_SMART_POLICY; - - private LinkedList triggerListeners = new LinkedList(); - - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Constructors. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - /** - *

- * Create a Trigger with no specified name, group, or {@link 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}. - *

- */ - public Trigger() { - // do nothing... - } - - /** - *

- * 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}. - *

- * - * @param group if 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. - *

- * - * @param group if 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. - *

- */ - public String getName() { - return name; - } - - /** - *

- * Set the name of this Trigger. - *

- * - * @exception IllegalArgumentException - * if name is null or empty. - */ - public void setName(String name) { - if (name == null || name.trim().length() == 0) - throw new IllegalArgumentException( - "Trigger name cannot be null or empty."); - - this.name = name; - } - - /** - *

- * Get the group of this Trigger. - *

- */ - public String getGroup() { - return group; - } - - /** - *

- * Set the name of this Trigger. - *

- * - * @param group if 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 org.quartz.JobDetail}. - *

- */ - public String getJobName() { - return jobName; - } - - /** - *

- * Set the name of the associated {@link org.quartz.JobDetail}. - *

- * - * @exception IllegalArgumentException - * if jobName is null or empty. - */ - public void setJobName(String jobName) { - if (jobName == null || jobName.trim().length() == 0) - throw new IllegalArgumentException( - "Job name cannot be null or empty."); - - this.jobName = jobName; - } - - /** - *

- * Get the name of the associated {@link org.quartz.JobDetail}'s - * group. - *

- */ - public String getJobGroup() { - return jobGroup; - } - - /** - *

- * Set the name of the associated {@link org.quartz.JobDetail}'s - * group. - *

- * - * @param group if 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". - *

- */ - public String getFullName() { - return group + "." + name; - } - - /** - *

- * Returns the 'full name' of the Job that the Trigger - * points to, in the format "group.name". - *

- */ - public String getFullJobName() { - return jobGroup + "." + jobName; - } - - /** - *

* Return the description given to the Trigger instance by * its creator (if any). - *

* * @return null if no description was set. */ - public String getDescription() { - return description; - } + public String getDescription(); /** - *

- * 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. - *

- */ - public void setDescription(String description) { - this.description = description; - } - - /** - *

- * Set whether or not the Trigger should be persisted in the - * {@link org.quartz.spi.JobStore} for re-use after program - * restarts. - *

- */ - public void setVolatility(boolean volatility) { - this.volatility = volatility; - } - - /** - *

- * Associate the {@link Calendar} with the given name with - * this Trigger. - *

- * - * @param calendarName - * use 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. - *

* * @return null if there is no associated Calendar. */ - public String getCalendarName() { - return calendarName; - } + public String getCalendarName(); /** - *

* 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. *

*/ - public JobDataMap getJobDataMap() { - if (jobDataMap == null) jobDataMap = new JobDataMap(); - return jobDataMap; - } + public JobDataMap getJobDataMap(); - /** - *

- * Set the JobDataMap to be associated with the - * Trigger. - *

- */ - public void setJobDataMap(JobDataMap jobDataMap) { - this.jobDataMap = jobDataMap; - } - - /** - *

- * Whether or not the Trigger should be persisted in the - * {@link org.quartz.spi.JobStore} for re-use after program - * restarts. - *

+ * The priority of a Trigger acts as a tiebreaker such that if + * two Triggers 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 false. + * If not explicitly set, the default value is 5. *

* - * @return true if the Trigger should be - * garbage collected along with the {@link Scheduler}. + * @see #DEFAULT_PRIORITY */ - public boolean isVolatile() { - return volatility; - } + public int getPriority(); /** - *

- * Add the specified name of a {@link TriggerListener} to - * the end of the Trigger's list of listeners. - *

- */ - public void addTriggerListener(String name) { - triggerListeners.add(name); - } - - /** - *

- * Remove the specified name of a {@link TriggerListener} - * from the Trigger's list of listeners. - *

- * - * @return true if the given name was found in the list, and removed - */ - public boolean removeTriggerListener(String name) { - return triggerListeners.remove(name); - } - - /** - *

- * Returns an array of String s containing the names of all - * {@link TriggerListener} assigned to the Trigger, - * in the order in which they should be notified. - *

- */ - public String[] getTriggerListenerNames() { - String[] outNames = new String[triggerListeners.size()]; - return (String[]) triggerListeners.toArray(outNames); - } - - /** - *

- * 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). - *

- * - * @see #executionComplete(JobExecutionContext, JobExecutionException) - */ - public abstract void triggered(Calendar calendar); - - /** - *

- * 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. - *

- * - * @return the first time at which the 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 org.quartz.JobDetail} associated with the Trigger - * in order to get the final instruction code from the trigger. - *

- * - * @param context - * is the 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 org.quartz.spi.JobStore}. *

*/ - public abstract boolean mayFireAgain(); + public boolean mayFireAgain(); /** - *

* Get the time at which the Trigger should occur. - *

*/ - public abstract Date getStartTime(); + public Date getStartTime(); - 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. - *

+ * regardless of any remaining repeats (based on the trigger's particular + * repeat settings). * * @see #getFinalFireTime() */ - public abstract Date getEndTime(); + public Date getEndTime(); /** - *

- * Returns the next time at which the Trigger will fire. If - * the trigger will not fire again, null will be returned. - * The value returned is not guaranteed to be valid until after the Trigger + * 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. *

+ * + * @see TriggerUtils#computeFireTimesBetween(org.quartz.spi.OperableTrigger, Calendar, java.util.Date, java.util.Date) */ - public abstract Date getNextFireTime(); + public Date getNextFireTime(); /** - *

- * Returns the previous time at which the Trigger will fire. + * Returns the previous time at which the Trigger fired. * If the trigger has not yet fired, null will be returned. */ - public abstract Date getPreviousFireTime(); + public 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. - *

*/ - public abstract Date getFireTimeAfter(Date afterTime); + public Date getFireTimeAfter(Date afterTime); /** - *

* 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(); + public 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. - *

- * - * @see #MISFIRE_INSTRUCTION_SMART_POLICY - * @see #updateAfterMisfire(Calendar) - * @see SimpleTrigger - * @see CronTrigger - */ - public void setMisfireInstruction(int misfireInstruction) { - if (!validateMisfireInstruction(misfireInstruction)) - throw new IllegalArgumentException( - "The misfire instruction code is invalid for this type of trigger."); - this.misfireInstruction = misfireInstruction; - } - - protected abstract boolean validateMisfireInstruction(int misfireInstruction); - - /** - *

* 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. - *

+ * constants that may be set as this property's value. * *

* If not explicitly set, the default value is MISFIRE_INSTRUCTION_SMART_POLICY. *

* * @see #MISFIRE_INSTRUCTION_SMART_POLICY - * @see #updateAfterMisfire(Calendar) * @see SimpleTrigger * @see CronTrigger */ - public int getMisfireInstruction() { - return misfireInstruction; - } + public int getMisfireInstruction(); /** - *

- * This method should not be used by the Quartz client. - *

+ * Get a {@link TriggerBuilder} that is configured to produce a + * Trigger identical to this one. * - *

- * 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. - *

+ * @see #getScheduleBuilder() */ - public abstract void updateAfterMisfire(Calendar cal); - + public TriggerBuilder getTriggerBuilder(); + /** - *

- * This method should not be used by the Quartz client. - *

+ * Get a {@link ScheduleBuilder} that is configured to produce a + * schedule identical to this trigger's schedule. * - *

- * 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). - *

- * - * @param cal + * @see #getTriggerBuilder() */ - public abstract void updateWithNewCalendar(Calendar cal, long misfireThreshold); + public ScheduleBuilder getScheduleBuilder(); /** - *

- * Validates whether the properties of the JobDetail are - * valid for submission into a Scheduler. + * Trigger equality is based upon the equality of the TriggerKey. * - * @throws IllegalStateException - * if a required property (such as Name, Group, Class) is not - * set. + * @return true if the key of this Trigger equals that of the given Trigger. */ - 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); - } - + public boolean equals(Object other); + /** *

- * This method should not be used by the Quartz client. + * Compare the next fire time of this Trigger to that of + * another by comparing their keys, or in other words, sorts them + * according to the natural (i.e. alphabetical) order of their keys. *

- * - *

- * Usable by {@link org.quartz.spi.JobStore} - * implementations, in order to facilitate 'recognizing' instances of fired - * Trigger s as their jobs complete execution. - *

- * - * */ - public void setFireInstanceId(String id) { - this.fireInstanceId = id; - } + public int compareTo(Trigger other); /** - *

- * This method should not be used by the Quartz client. - *

+ * A Comparator that compares trigger's next fire times, or in other words, + * sorts them according to earliest next fire time. If the fire times are + * the same, then the triggers are sorted according to priority (highest + * value first), if the priorities are the same, then they are sorted + * by key. */ - public String getFireInstanceId() { - return fireInstanceId; - } + class TriggerTimeComparator implements Comparator, Serializable { + + private static final long serialVersionUID = -3904243490805975570L; + + // This static method exists for comparator in TC clustered quartz + public static int compare(Date nextFireTime1, int priority1, TriggerKey key1, Date nextFireTime2, int priority2, TriggerKey key2) { + if (nextFireTime1 != null || nextFireTime2 != null) { + if (nextFireTime1 == null) { + return 1; + } - /** - *

- * 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(); - } + if (nextFireTime2 == null) { + return -1; + } - /** - *

- * Compare the next fire time of this Trigger to that of - * another. - *

- */ - public int compareTo(Object obj) { - Trigger other = (Trigger) obj; + if(nextFireTime1.before(nextFireTime2)) { + return -1; + } - Date myTime = getNextFireTime(); - Date otherTime = other.getNextFireTime(); + if(nextFireTime1.after(nextFireTime2)) { + return 1; + } + } - if (myTime == null && otherTime == null) return 0; + int comp = priority2 - priority1; + if (comp != 0) { + return comp; + } - if (myTime == null) return 1; + return key1.compareTo(key2); + } - if (otherTime == null) return -1; - if(myTime.before(otherTime)) - return -1; - - if(myTime.after(otherTime)) - return 1; - - return 0; - } - - public boolean equals(Object obj) { - if (!(obj instanceof Trigger)) return false; - - Trigger other = (Trigger) obj; - - if (!other.getName().equals(getName())) return false; - if (!other.getGroup().equals(getGroup())) return false; - - return true; - } - - - public int hashCode() { - return getFullName().hashCode(); - } - - public Object clone() { - Trigger copy; - try { - copy = (Trigger) super.clone(); - } catch (CloneNotSupportedException ex) { - throw new IncompatibleClassChangeError("Not Cloneable."); + public int compare(Trigger t1, Trigger t2) { + return compare(t1.getNextFireTime(), t1.getPriority(), t1.getKey(), t2.getNextFireTime(), t2.getPriority(), t2.getKey()); } - return copy; } - } Index: 3rdParty_sources/quartz/org/quartz/TriggerBuilder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/TriggerBuilder.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/TriggerBuilder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,416 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import java.util.Date; + +import org.quartz.spi.MutableTrigger; +import org.quartz.utils.Key; + +/** + * TriggerBuilder is used to instantiate {@link Trigger}s. + * + *

The builder will always try to keep itself in a valid state, with + * reasonable defaults set for calling build() at any point. For instance + * if you do not invoke withSchedule(..) method, a default schedule + * of firing once immediately will be used. As another example, if you + * do not invoked withIdentity(..) a trigger name will be generated + * for you.

+ * + *

Quartz provides a builder-style API for constructing scheduling-related + * entities via a Domain-Specific Language (DSL). The DSL can best be + * utilized through the usage of static imports of the methods on the classes + * TriggerBuilder, JobBuilder, + * DateBuilder, JobKey, TriggerKey + * and the various ScheduleBuilder implementations.

+ * + *

Client code can then use the DSL to write code such as this:

+ *
+ *         JobDetail job = newJob(MyJob.class)
+ *             .withIdentity("myJob")
+ *             .build();
+ *             
+ *         Trigger trigger = newTrigger() 
+ *             .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+ *             .withSchedule(simpleSchedule()
+ *                 .withIntervalInHours(1)
+ *                 .repeatForever())
+ *             .startAt(futureDate(10, MINUTES))
+ *             .build();
+ *         
+ *         scheduler.scheduleJob(job, trigger);
+ * 
+ *  
+ * @see JobBuilder
+ * @see ScheduleBuilder
+ * @see DateBuilder 
+ * @see Trigger
+ */
+public class TriggerBuilder {
+
+    private TriggerKey key;
+    private String description;
+    private Date startTime = new Date();
+    private Date endTime;
+    private int priority = Trigger.DEFAULT_PRIORITY;
+    private String calendarName;
+    private JobKey jobKey;
+    private JobDataMap jobDataMap = new JobDataMap();
+    
+    private ScheduleBuilder scheduleBuilder = null;
+    
+    private TriggerBuilder() {
+        
+    }
+    
+    /**
+     * Create a new TriggerBuilder with which to define a 
+     * specification for a Trigger.
+     * 
+     * @return the new TriggerBuilder
+     */
+    public static TriggerBuilder newTrigger() {
+        return new TriggerBuilder();
+    }
+    
+    /**
+     * Produce the Trigger.
+     * 
+     * @return a Trigger that meets the specifications of the builder.
+     */
+    @SuppressWarnings("unchecked")
+    public T build() {
+
+        if(scheduleBuilder == null)
+            scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
+        MutableTrigger trig = scheduleBuilder.build();
+        
+        trig.setCalendarName(calendarName);
+        trig.setDescription(description);
+        trig.setStartTime(startTime);
+        trig.setEndTime(endTime);
+        if(key == null)
+            key = new TriggerKey(Key.createUniqueName(null), null);
+        trig.setKey(key); 
+        if(jobKey != null)
+            trig.setJobKey(jobKey);
+        trig.setPriority(priority);
+        
+        if(!jobDataMap.isEmpty())
+            trig.setJobDataMap(jobDataMap);
+        
+        return (T) trig;
+    }
+
+    /**
+     * Use a TriggerKey with the given name and default group to
+     * identify the Trigger.
+     * 
+     * 

If none of the 'withIdentity' methods are set on the TriggerBuilder, + * then a random, unique TriggerKey will be generated.

+ * + * @param name the name element for the Trigger's TriggerKey + * @return the updated TriggerBuilder + * @see TriggerKey + * @see Trigger#getKey() + */ + public TriggerBuilder withIdentity(String name) { + key = new TriggerKey(name, null); + return this; + } + + /** + * Use a TriggerKey with the given name and group to + * identify the Trigger. + * + *

If none of the 'withIdentity' methods are set on the TriggerBuilder, + * then a random, unique TriggerKey will be generated.

+ * + * @param name the name element for the Trigger's TriggerKey + * @param group the group element for the Trigger's TriggerKey + * @return the updated TriggerBuilder + * @see TriggerKey + * @see Trigger#getKey() + */ + public TriggerBuilder withIdentity(String name, String group) { + key = new TriggerKey(name, group); + return this; + } + + /** + * Use the given TriggerKey to identify the Trigger. + * + *

If none of the 'withIdentity' methods are set on the TriggerBuilder, + * then a random, unique TriggerKey will be generated.

+ * + * @param triggerKey the TriggerKey for the Trigger to be built + * @return the updated TriggerBuilder + * @see TriggerKey + * @see Trigger#getKey() + */ + public TriggerBuilder withIdentity(TriggerKey triggerKey) { + this.key = triggerKey; + return this; + } + + /** + * Set the given (human-meaningful) description of the Trigger. + * + * @param triggerDescription the description for the Trigger + * @return the updated TriggerBuilder + * @see Trigger#getDescription() + */ + public TriggerBuilder withDescription(String triggerDescription) { + this.description = triggerDescription; + return this; + } + + /** + * Set the Trigger's priority. When more than one Trigger have the same + * fire time, the scheduler will fire the one with the highest priority + * first. + * + * @param triggerPriority the priority for the Trigger + * @return the updated TriggerBuilder + * @see Trigger#DEFAULT_PRIORITY + * @see Trigger#getPriority() + */ + public TriggerBuilder withPriority(int triggerPriority) { + this.priority = triggerPriority; + return this; + } + + /** + * Set the name of the {@link Calendar} that should be applied to this + * Trigger's schedule. + * + * @param calName the name of the Calendar to reference. + * @return the updated TriggerBuilder + * @see Calendar + * @see Trigger#getCalendarName() + */ + public TriggerBuilder modifiedByCalendar(String calName) { + this.calendarName = calName; + return this; + } + + /** + * Set the time the Trigger should start at - the trigger may or may + * not fire at this time - depending upon the schedule configured for + * the Trigger. However the Trigger will NOT fire before this time, + * regardless of the Trigger's schedule. + * + * @param triggerStartTime the start time for the Trigger. + * @return the updated TriggerBuilder + * @see Trigger#getStartTime() + * @see DateBuilder + */ + public TriggerBuilder startAt(Date triggerStartTime) { + this.startTime = triggerStartTime; + return this; + } + + /** + * Set the time the Trigger should start at to the current moment - + * the trigger may or may not fire at this time - depending upon the + * schedule configured for the Trigger. + * + * @return the updated TriggerBuilder + * @see Trigger#getStartTime() + */ + public TriggerBuilder startNow() { + this.startTime = new Date(); + return this; + } + + /** + * Set the time at which the Trigger will no longer fire - even if it's + * schedule has remaining repeats. + * + * @param triggerEndTime the end time for the Trigger. If null, the end time is indefinite. + * @return the updated TriggerBuilder + * @see Trigger#getEndTime() + * @see DateBuilder + */ + public TriggerBuilder endAt(Date triggerEndTime) { + this.endTime = triggerEndTime; + return this; + } + + /** + * Set the {@link ScheduleBuilder} that will be used to define the + * Trigger's schedule. + * + *

The particular SchedulerBuilder used will dictate + * the concrete type of Trigger that is produced by the TriggerBuilder.

+ * + * @param schedBuilder the SchedulerBuilder to use. + * @return the updated TriggerBuilder + * @see ScheduleBuilder + * @see SimpleScheduleBuilder + * @see CronScheduleBuilder + * @see CalendarIntervalScheduleBuilder + */ + @SuppressWarnings("unchecked") + public TriggerBuilder withSchedule(ScheduleBuilder schedBuilder) { + this.scheduleBuilder = schedBuilder; + return (TriggerBuilder) this; + } + + /** + * Set the identity of the Job which should be fired by the produced + * Trigger. + * + * @param keyOfJobToFire the identity of the Job to fire. + * @return the updated TriggerBuilder + * @see Trigger#getJobKey() + */ + public TriggerBuilder forJob(JobKey keyOfJobToFire) { + this.jobKey = keyOfJobToFire; + return this; + } + + /** + * Set the identity of the Job which should be fired by the produced + * Trigger - a JobKey will be produced with the given + * name and default group. + * + * @param jobName the name of the job (in default group) to fire. + * @return the updated TriggerBuilder + * @see Trigger#getJobKey() + */ + public TriggerBuilder forJob(String jobName) { + this.jobKey = new JobKey(jobName, null); + return this; + } + + /** + * Set the identity of the Job which should be fired by the produced + * Trigger - a JobKey will be produced with the given + * name and group. + * + * @param jobName the name of the job to fire. + * @param jobGroup the group of the job to fire. + * @return the updated TriggerBuilder + * @see Trigger#getJobKey() + */ + public TriggerBuilder forJob(String jobName, String jobGroup) { + this.jobKey = new JobKey(jobName, jobGroup); + return this; + } + + /** + * Set the identity of the Job which should be fired by the produced + * Trigger, by extracting the JobKey from the given job. + * + * @param jobDetail the Job to fire. + * @return the updated TriggerBuilder + * @see Trigger#getJobKey() + */ + public TriggerBuilder forJob(JobDetail jobDetail) { + JobKey k = jobDetail.getKey(); + if(k.getName() == null) + throw new IllegalArgumentException("The given job has not yet had a name assigned to it."); + this.jobKey = k; + return this; + } + + /** + * Add the given key-value pair to the Trigger's {@link JobDataMap}. + * + * @return the updated TriggerBuilder + * @see Trigger#getJobDataMap() + */ + public TriggerBuilder usingJobData(String dataKey, String value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the Trigger's {@link JobDataMap}. + * + * @return the updated TriggerBuilder + * @see Trigger#getJobDataMap() + */ + public TriggerBuilder usingJobData(String dataKey, Integer value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the Trigger's {@link JobDataMap}. + * + * @return the updated TriggerBuilder + * @see Trigger#getJobDataMap() + */ + public TriggerBuilder usingJobData(String dataKey, Long value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the Trigger's {@link JobDataMap}. + * + * @return the updated TriggerBuilder + * @see Trigger#getJobDataMap() + */ + public TriggerBuilder usingJobData(String dataKey, Float value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the Trigger's {@link JobDataMap}. + * + * @return the updated TriggerBuilder + * @see Trigger#getJobDataMap() + */ + public TriggerBuilder usingJobData(String dataKey, Double value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Add the given key-value pair to the Trigger's {@link JobDataMap}. + * + * @return the updated TriggerBuilder + * @see Trigger#getJobDataMap() + */ + public TriggerBuilder usingJobData(String dataKey, Boolean value) { + jobDataMap.put(dataKey, value); + return this; + } + + /** + * Set the Trigger's {@link JobDataMap}, adding any values to it + * that were already set on this TriggerBuilder using any of the + * other 'usingJobData' methods. + * + * @return the updated TriggerBuilder + * @see Trigger#getJobDataMap() + */ + public TriggerBuilder usingJobData(JobDataMap newJobDataMap) { + // add any existing data to this new map + for(String dataKey: jobDataMap.keySet()) { + newJobDataMap.put(dataKey, jobDataMap.get(dataKey)); + } + jobDataMap = newJobDataMap; // set new map as the map to use + return this; + } + +} Index: 3rdParty_sources/quartz/org/quartz/TriggerKey.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/TriggerKey.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/TriggerKey.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,78 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz; + +import org.quartz.utils.Key; + +/** + * Uniquely identifies a {@link Trigger}. + * + *

Keys are composed of both a name and group, and the name must be unique + * within the group. If only a name is specified then the default group + * name will be used.

+ * + * + *

Quartz provides a builder-style API for constructing scheduling-related + * entities via a Domain-Specific Language (DSL). The DSL can best be + * utilized through the usage of static imports of the methods on the classes + * TriggerBuilder, JobBuilder, + * DateBuilder, JobKey, TriggerKey + * and the various ScheduleBuilder implementations.

+ * + *

Client code can then use the DSL to write code such as this:

+ *
+ *         JobDetail job = newJob(MyJob.class)
+ *             .withIdentity("myJob")
+ *             .build();
+ *             
+ *         Trigger trigger = newTrigger() 
+ *             .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+ *             .withSchedule(simpleSchedule()
+ *                 .withIntervalInHours(1)
+ *                 .repeatForever())
+ *             .startAt(futureDate(10, MINUTES))
+ *             .build();
+ *         
+ *         scheduler.scheduleJob(job, trigger);
+ * 
+ *  
+ * 
+ * @see Trigger
+ * @see Key#DEFAULT_GROUP
+ */
+public final class TriggerKey extends Key {
+  
+    private static final long serialVersionUID = 8070357886703449660L;
+
+    public TriggerKey(String name) {
+        super(name, null);
+    }
+
+    public TriggerKey(String name, String group) {
+        super(name, group);
+    }
+
+    public static TriggerKey triggerKey(String name) {
+        return new TriggerKey(name, null);
+    }
+    
+    public static TriggerKey triggerKey(String name, String group) {
+        return new TriggerKey(name, group);
+    }
+    
+}
Index: 3rdParty_sources/quartz/org/quartz/TriggerListener.java
===================================================================
diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b
--- 3rdParty_sources/quartz/org/quartz/TriggerListener.java	(.../TriggerListener.java)	(revision 2e3463e873227c6a3edcb3e02d55270219e553ff)
+++ 3rdParty_sources/quartz/org/quartz/TriggerListener.java	(.../TriggerListener.java)	(revision c208628989d52041b3765784f4c8cbfd6c80d47b)
@@ -1,6 +1,6 @@
 
 /* 
- * Copyright 2004-2005 OpenSymphony 
+ * Copyright 2001-2009 Terracotta, Inc. 
  * 
  * 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 
@@ -16,19 +16,17 @@
  * 
  */
 
-/*
- * Previously Copyright (c) 2001-2004 James House
- */
 package org.quartz;
 
+import org.quartz.Trigger.CompletedExecutionInstruction;
+
 /**
- * 

* 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. - *

* - * @see Scheduler + * @see ListenerManager#addTriggerListener(TriggerListener, Matcher) + * @see Matcher * @see Trigger * @see JobListener * @see JobExecutionContext @@ -50,7 +48,7 @@ * Get the name of the TriggerListener. *

*/ - public String getName(); + String getName(); /** *

@@ -70,13 +68,14 @@ * The JobExecutionContext that will be passed to * the Job'sexecute(xx) method. */ - public void triggerFired(Trigger trigger, JobExecutionContext context); + void triggerFired(Trigger trigger, JobExecutionContext context); /** *

* Called by the {@link Scheduler} when a {@link Trigger} * has fired, and it's associated {@link org.quartz.JobDetail} - * is about to be executed. + * is about to be executed. If the implementation vetos the execution (via + * returning true), the job's execute method will not be called. *

* *

@@ -90,7 +89,7 @@ * The JobExecutionContext that will be passed to * the Job'sexecute(xx) method. */ - public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context); + boolean vetoJobExecution(Trigger trigger, JobExecutionContext context); /** @@ -109,7 +108,7 @@ * @param trigger * The Trigger that has misfired. */ - public void triggerMisfired(Trigger trigger); + void triggerMisfired(Trigger trigger); /** *

@@ -124,11 +123,11 @@ * @param context * The JobExecutionContext that was passed to the * Job'sexecute(xx) method. - * @param triggerIntructionCode + * @param triggerInstructionCode * the result of the call on the Trigger'striggered(xx) * method. */ - public void triggerComplete(Trigger trigger, JobExecutionContext context, - int triggerInstructionCode); + void triggerComplete(Trigger trigger, JobExecutionContext context, + CompletedExecutionInstruction triggerInstructionCode); } Index: 3rdParty_sources/quartz/org/quartz/TriggerUtils.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/TriggerUtils.java (.../TriggerUtils.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/TriggerUtils.java (.../TriggerUtils.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,31 +16,21 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz; -import java.util.Calendar; import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.TimeZone; +import org.quartz.spi.OperableTrigger; + /** - *

- * Convenience and utility methods for simplifying the construction and - * configuration of {@link Trigger}s. - *

+ * Convenience and utility methods for working with {@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 + * @see DateBuilder * * @author James House */ @@ -54,30 +44,12 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - 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() { + } + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -86,1134 +58,48 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - 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). - *

+ * 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 trig the tigger to change name to - * @param name the new trigger name + * @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 void setTriggerIdentity(Trigger trig, String name) { - setTriggerIdentity(trig, name, Scheduler.DEFAULT_GROUP); - } + public static List computeFireTimes(OperableTrigger trigg, org.quartz.Calendar cal, + int numTimes) { + LinkedList lst = new LinkedList(); - /** - *

- * Set the given Trigger's name to the given value, and its - * group to the given group. - *

- * - * @param trig the tigger to change name to - * @param name the new trigger name - * @param group the new trigger group - */ - public static void setTriggerIdentity( - Trigger trig, String name, String group) { - trig.setName(name); - trig.setGroup(group); - } + OperableTrigger t = (OperableTrigger) trigg.clone(); - /** - *

- * 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... */ + if (t.getNextFireTime() == null) { + t.computeFirstFireTime(cal); } - 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... */ + for (int i = 0; i < numTimes; i++) { + Date d = t.getNextFireTime(); + if (d != null) { + lst.add(d); + t.triggered(cal); + } else { + break; + } } - - trig.setStartTime(new Date()); - return trig; + return java.util.Collections.unmodifiableList(lst); } - - /** - *

- * 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'. - *

+ * Compute the Date that is 1 second after the Nth firing of + * the given Trigger, taking the triger's associated + * Calendar into consideration. * - * @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); - - 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, if null 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, if null 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, if null 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, if null 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, if null 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, if null 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 TimeMinute-BaseResult Time
11:16:412011:20:00
11:36:412011:40:00
11:46:412012:00:00
11:26:413011:30:00
11:36:413012:00:00
11:16:411711:17:00
11:17:411711:34:00
11:52:411712:00:00
11:52:41511:55:00
11:57:41512:00:00
11:17:41012:00:00
11:17:41111: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 - * @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(..). - *

- * - * @param date the Date to round, if 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. - *

- * - * @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 - * @return the new date - */ - 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 - * @return the new date - */ - 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 - * @return the new date - */ - 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. * @@ -1223,28 +109,38 @@ * 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 + * @return the computed Date, or null if the trigger (as configured) will not fire that many times. */ - public static List computeFireTimes(Trigger trigg, org.quartz.Calendar cal, + public static Date computeEndTimeToAllowParticularNumberOfFirings(OperableTrigger trigg, org.quartz.Calendar cal, int numTimes) { - LinkedList lst = new LinkedList(); - Trigger t = (Trigger) trigg.clone(); + OperableTrigger t = (OperableTrigger) trigg.clone(); if (t.getNextFireTime() == null) { t.computeFirstFireTime(cal); } - + + int c = 0; + Date endTime = null; + for (int i = 0; i < numTimes; i++) { Date d = t.getNextFireTime(); if (d != null) { - lst.add(d); + c++; t.triggered(cal); - } else + if(c == numTimes) + endTime = d; + } else { break; + } } - - return java.util.Collections.unmodifiableList(lst); + + if(endTime == null) + return null; + + endTime = new Date(endTime.getTime() + 1000L); + + return endTime; } /** @@ -1256,7 +152,7 @@ * *

* 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 + * date range, then firings which have already occurred will not be listed * in the output List. *

* @@ -1270,96 +166,36 @@ * The ending date at which to stop finding fire times * @return List of java.util.Date objects */ - public static List computeFireTimesBetween(Trigger trigg, + public static List computeFireTimesBetween(OperableTrigger trigg, org.quartz.Calendar cal, Date from, Date to) { - LinkedList lst = new LinkedList(); + LinkedList lst = new LinkedList(); - Trigger t = (Trigger) trigg.clone(); + OperableTrigger t = (OperableTrigger) 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; + if (d.after(to)) { + break; + } lst.add(d); t.triggered(cal); - } else + } 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; - } } Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/UICronTrigger.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/UnableToInterruptJobException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/UnableToInterruptJobException.java (.../UnableToInterruptJobException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/UnableToInterruptJobException.java (.../UnableToInterruptJobException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,23 +16,19 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ - package org.quartz; /** - *

* An exception that is thrown to indicate that a call to * InterruptableJob.interrupt() failed without interrupting the Job. - *

* * @see org.quartz.InterruptableJob#interrupt() * * @author James House */ public class UnableToInterruptJobException extends SchedulerException { + + private static final long serialVersionUID = -490863760696463776L; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -56,7 +52,7 @@ * Create a UnableToInterruptJobException with the given cause. *

*/ - public UnableToInterruptJobException(Exception cause) { + public UnableToInterruptJobException(Throwable cause) { super(cause); } Index: 3rdParty_sources/quartz/org/quartz/commonj/WorkManagerThreadExecutor.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/commonj/WorkManagerThreadExecutor.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/commonj/WorkManagerThreadExecutor.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,111 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.commonj; + +import commonj.work.Work; +import commonj.work.WorkManager; +import org.quartz.spi.ThreadExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.naming.InitialContext; +import javax.naming.NamingException; + +/** + * CommonJ WorkManager implementation of hacked Quartz ThreadExecutor class. + * This class schedules work on a WorkManager which is looked up in JNDI. The + * advantage is that all the work performed is done on a managed thread which is + * required by WebSphere, see QUARTZ-743 for + * details. + * + * @author matt.accola + * @version $Revision$ $Date$ + */ +public class WorkManagerThreadExecutor implements ThreadExecutor { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private String workManagerName; + private WorkManager workManager; + + public void execute(Thread thread) { + Work work = new org.quartz.commonj.DelegatingWork(thread); + try { + this.workManager.schedule(work); + } catch (Exception e) { + log.error("Error attempting to schedule QuartzSchedulerThread: " + e.getMessage(), e); + } + } + + public void initialize() { + try { + this.workManager = (WorkManager) new InitialContext().lookup(workManagerName); + } catch (NamingException e) { + throw new IllegalStateException("Could not locate WorkManager: " + e.getMessage(), e); + } + } + + /** + * Sets the JNDI name of the work manager to use. + * + * @param workManagerName the JNDI name to use to lookup the work manager + */ + public void setWorkManagerName(String workManagerName) { + this.workManagerName = workManagerName; + } + +} + +class DelegatingWork implements Work { + + private final Runnable delegate; + + /** + * Create a new DelegatingWork. + * + * @param delegate the Runnable implementation to delegate to + */ + public DelegatingWork(Runnable delegate) { + this.delegate = delegate; + } + + /** + * @return the wrapped Runnable implementation. + */ + public final Runnable getDelegate() { + return this.delegate; + } + + /** + * Delegates execution to the underlying Runnable. + */ + public void run() { + this.delegate.run(); + } + + public boolean isDaemon() { + return false; + } + + /** + * This implementation is empty, since we expect the Runnable to terminate + * based on some specific shutdown signal. + */ + public void release() { + } + +} Index: 3rdParty_sources/quartz/org/quartz/core/JobRunShell.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/core/JobRunShell.java (.../JobRunShell.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/core/JobRunShell.java (.../JobRunShell.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,37 +1,36 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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.core; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.quartz.Job; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; -import org.quartz.JobPersistenceException; import org.quartz.Scheduler; import org.quartz.SchedulerException; -import org.quartz.Trigger; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.impl.JobExecutionContextImpl; +import org.quartz.listeners.SchedulerListenerSupport; +import org.quartz.spi.OperableTrigger; import org.quartz.spi.TriggerFiredBundle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** *

@@ -41,372 +40,343 @@ * 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. *

- * + * * @see JobRunShellFactory * @see org.quartz.core.QuartzSchedulerThread * @see org.quartz.Job * @see org.quartz.Trigger - * + * * @author James House */ -public class JobRunShell implements Runnable { +public class JobRunShell extends SchedulerListenerSupport implements Runnable { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Data members. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - protected JobExecutionContext jec = null; + protected JobExecutionContextImpl jec = null; protected QuartzScheduler qs = null; + + protected TriggerFiredBundle firedTriggerBundle = null; protected Scheduler scheduler = null; - protected SchedulingContext schdCtxt = null; + protected volatile boolean shutdownRequested = false; - protected JobRunShellFactory jobRunShellFactory = null; + private final Logger log = LoggerFactory.getLogger(getClass()); - protected boolean shutdownRequested = false; - - protected Log log = LogFactory.getLog(getClass()); - /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Constructors. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** *

* Create a JobRunShell instance with the given settings. *

- * - * @param jobRunShellFactory - * A handle to the JobRunShellFactory 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; + public JobRunShell(Scheduler scheduler, TriggerFiredBundle bndle) { this.scheduler = scheduler; - this.schdCtxt = schdCtxt; + this.firedTriggerBundle = bndle; } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Interface. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private static Log getLog() - { - return LogFactory.getLog(JobRunShell.class); + @Override + public void schedulerShuttingdown() { + requestShutdown(); } - - public void initialize(QuartzScheduler qs, TriggerFiredBundle firedBundle) - throws SchedulerException { - this.qs = qs; + @Override + protected Logger getLog() { + return log; + } + + public void initialize(QuartzScheduler sched) + throws SchedulerException { + this.qs = sched; + Job job = null; - JobDetail jobDetail = firedBundle.getJobDetail(); + JobDetail jobDetail = firedTriggerBundle.getJobDetail(); try { - job = qs.getJobFactory().newJob(firedBundle); + job = sched.getJobFactory().newJob(firedTriggerBundle, scheduler); } catch (SchedulerException se) { - qs.notifySchedulerListenersError( + sched.notifySchedulerListenersError( "An error occured instantiating job to be executed. job= '" - + jobDetail.getFullName() + "'", se); + + jobDetail.getKey() + "'", se); throw se; - } catch (Exception e) { - SchedulerException se = new SchedulerException( - "Problem instantiating class '" - + jobDetail.getJobClass().getName() + "'", e); - 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( + + jobDetail.getJobClass().getName() + "' - ", ncdfe); + sched.notifySchedulerListenersError( "An error occured instantiating job to be executed. job= '" - + jobDetail.getFullName() + "'", se); + + jobDetail.getKey() + "'", se); throw se; } - this.jec = new JobExecutionContext(scheduler, firedBundle, job); + this.jec = new JobExecutionContextImpl(scheduler, firedTriggerBundle, job); } public void requestShutdown() { shutdownRequested = true; } public void run() { - Trigger trigger = jec.getTrigger(); - JobDetail jobDetail = jec.getJobDetail(); + qs.addInternalSchedulerListener(this); - do { + try { + OperableTrigger trigger = (OperableTrigger) jec.getTrigger(); + JobDetail jobDetail = jec.getJobDetail(); - JobExecutionException jobExEx = null; - Job job = jec.getJobInstance(); + do { - try { - begin(); - } catch (SchedulerException se) { - qs.notifySchedulerListenersError("Error executing Job (" - + jec.getJobDetail().getFullName() - + ": couldn't begin execution.", se); - break; - } + JobExecutionException jobExEx = null; + Job job = jec.getJobInstance(); - // notify job & trigger listeners... - try { - if (!notifyListenersBeginning(jec)) break; - } - catch(VetoedException ve) { try { - complete(true); + begin(); } catch (SchedulerException se) { - qs.notifySchedulerListenersError("Error during veto of Job (" - + jec.getJobDetail().getFullName() - + ": couldn't finalize execution.", se); + qs.notifySchedulerListenersError("Error executing Job (" + + jec.getJobDetail().getKey() + + ": couldn't begin execution.", se); + break; } - 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 (Exception 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 job & trigger listeners... + try { + if (!notifyListenersBeginning(jec)) { + break; + } + } catch(VetoedException ve) { + try { + CompletedExecutionInstruction instCode = trigger.executionComplete(jec, null); + qs.notifyJobStoreJobVetoed(trigger, jobDetail, instCode); + + // QTZ-205 + // Even if trigger got vetoed, we still needs to check to see if it's the trigger's finalized run or not. + if (jec.getTrigger().getNextFireTime() == null) { + qs.notifySchedulerListenersFinalized(jec.getTrigger()); + } - // notify all job listeners - if (!notifyJobListenersComplete(jec, jobExEx)) break; + complete(true); + } catch (SchedulerException se) { + qs.notifySchedulerListenersError("Error during veto of Job (" + + jec.getJobDetail().getKey() + + ": couldn't finalize execution.", se); + } + break; + } - int instCode = Trigger.INSTRUCTION_NOOP; + long startTime = System.currentTimeMillis(); + long endTime = startTime; - // 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); - } + // execute the job + try { + log.debug("Calling execute on job " + jobDetail.getKey()); + job.execute(jec); + endTime = System.currentTimeMillis(); + } catch (JobExecutionException jee) { + endTime = System.currentTimeMillis(); + jobExEx = jee; + getLog().info("Job " + jobDetail.getKey() + + " threw a JobExecutionException: ", jobExEx); + } catch (Throwable e) { + endTime = System.currentTimeMillis(); + getLog().error("Job " + jobDetail.getKey() + + " threw an unhandled Exception: ", e); + SchedulerException se = new SchedulerException( + "Job threw an unhandled exception.", e); + qs.notifySchedulerListenersError("Job (" + + jec.getJobDetail().getKey() + + " threw an exception.", se); + jobExEx = new JobExecutionException(se, false); + } - // notify all trigger listeners - if (!notifyTriggerListenersComplete(jec, instCode)) break; + jec.setJobRunTime(endTime - startTime); - // update job/trigger or re-execute job - if (instCode == Trigger.INSTRUCTION_RE_EXECUTE_JOB) { - jec.incrementRefireCount(); + // notify all job listeners + if (!notifyJobListenersComplete(jec, jobExEx)) { + break; + } + + CompletedExecutionInstruction instCode = CompletedExecutionInstruction.NOOP; + + // update the trigger try { - complete(false); + 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); + 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 == CompletedExecutionInstruction.RE_EXECUTE_JOB) { + jec.incrementRefireCount(); + try { + complete(false); + } catch (SchedulerException se) { + qs.notifySchedulerListenersError("Error executing Job (" + + jec.getJobDetail().getKey() + + ": couldn't finalize execution.", se); + } + continue; + } + + try { + complete(true); } catch (SchedulerException se) { qs.notifySchedulerListenersError("Error executing Job (" - + jec.getJobDetail().getFullName() + + jec.getJobDetail().getKey() + ": couldn't finalize execution.", se); + continue; } - continue; - } - try { - complete(true); - } catch (SchedulerException se) { - qs.notifySchedulerListenersError("Error executing Job (" - + jec.getJobDetail().getFullName() - + ": couldn't finalize execution.", se); - continue; - } + qs.notifyJobStoreJobComplete(trigger, jobDetail, instCode); + break; + } while (true); - 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); - - qs.notifySchedulerThread(); - - jobRunShellFactory.returnJobRunShell(this); + } finally { + qs.removeInternalSchedulerListener(this); + } } protected void begin() throws SchedulerException { } protected void complete(boolean successfulExecution) - throws SchedulerException { + throws SchedulerException { } public void passivate() { jec = null; qs = null; } - private boolean notifyListenersBeginning(JobExecutionContext jec) throws VetoedException { - + private boolean notifyListenersBeginning(JobExecutionContext jobExCtxt) throws VetoedException { + boolean vetoed = false; - + // notify all trigger listeners try { - vetoed = qs.notifyTriggerListenersFired(jec); + vetoed = qs.notifyTriggerListenersFired(jobExCtxt); } 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); + + jobExCtxt.getTrigger().getKey() + " job= " + + jobExCtxt.getJobDetail().getKey(), se); return false; } if(vetoed) { try { - qs.notifyJobListenersWasVetoed(jec); + qs.notifyJobListenersWasVetoed(jobExCtxt); } 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); + + jobExCtxt.getTrigger().getKey() + " job= " + + jobExCtxt.getJobDetail().getKey(), se); } throw new VetoedException(); } - + // notify all job listeners try { - qs.notifyJobListenersToBeExecuted(jec); + qs.notifyJobListenersToBeExecuted(jobExCtxt); } 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); + + jobExCtxt.getTrigger().getKey() + " job= " + + jobExCtxt.getJobDetail().getKey(), se); return false; } return true; } - private boolean notifyJobListenersComplete(JobExecutionContext jec, - JobExecutionException jobExEx) { + private boolean notifyJobListenersComplete(JobExecutionContext jobExCtxt, JobExecutionException jobExEx) { try { - qs.notifyJobListenersWasExecuted(jec, jobExEx); + qs.notifyJobListenersWasExecuted(jobExCtxt, 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); + + jobExCtxt.getTrigger().getKey() + " job= " + + jobExCtxt.getJobDetail().getKey(), se); return false; } return true; } - private boolean notifyTriggerListenersComplete(JobExecutionContext jec, - int instCode) { + private boolean notifyTriggerListenersComplete(JobExecutionContext jobExCtxt, CompletedExecutionInstruction instCode) { try { - qs.notifyTriggerListenersComplete(jec, instCode); + qs.notifyTriggerListenersComplete(jobExCtxt, 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); + + jobExCtxt.getTrigger().getKey() + " job= " + + jobExCtxt.getJobDetail().getKey(), se); return false; } - if (jec.getTrigger().getNextFireTime() == null) - qs.notifySchedulerListenersFinalized(jec.getTrigger()); + if (jobExCtxt.getTrigger().getNextFireTime() == null) { + qs.notifySchedulerListenersFinalized(jobExCtxt.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; - } + static class VetoedException extends Exception { - - class VetoedException extends Exception - { - public VetoedException() - { + private static final long serialVersionUID = 1539955697495918463L; + + public VetoedException() { } } -} \ No newline at end of file + +} Index: 3rdParty_sources/quartz/org/quartz/core/JobRunShellFactory.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/core/JobRunShellFactory.java (.../JobRunShellFactory.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/core/JobRunShellFactory.java (.../JobRunShellFactory.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,29 +16,19 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.core; import org.quartz.Scheduler; import org.quartz.SchedulerConfigException; import org.quartz.SchedulerException; +import org.quartz.spi.TriggerFiredBundle; /** *

* Responsible for creating the instances of {@link JobRunShell} * to be used within the {@link QuartzScheduler} instance. *

* - *

- * 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. - *

- * * @author James House */ public interface JobRunShellFactory { @@ -55,28 +45,17 @@ *

* 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. + * the JobExecutionContext s within it. *

*/ - public void initialize(Scheduler scheduler, SchedulingContext schedCtxt) - throws SchedulerConfigException; + void initialize(Scheduler scheduler) + throws SchedulerConfigException; /** *

* Called by the {@link org.quartz.core.QuartzSchedulerThread} * to obtain instances of {@link JobRunShell}. *

*/ - public JobRunShell borrowJobRunShell() throws SchedulerException; - - /** - *

- * Called by the {@link org.quartz.core.QuartzSchedulerThread} - * to return instances of {@link JobRunShell}. - *

- */ - public void returnJobRunShell(JobRunShell jobRunShell); - + JobRunShell createJobRunShell(TriggerFiredBundle bundle) throws SchedulerException; } \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/core/ListenerManagerImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/ListenerManagerImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/ListenerManagerImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,275 @@ +package org.quartz.core; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.quartz.JobKey; +import org.quartz.JobListener; +import org.quartz.ListenerManager; +import org.quartz.Matcher; +import org.quartz.SchedulerListener; +import org.quartz.TriggerKey; +import org.quartz.TriggerListener; +import org.quartz.impl.matchers.EverythingMatcher; + +public class ListenerManagerImpl implements ListenerManager { + + private Map globalJobListeners = new LinkedHashMap(10); + + private Map globalTriggerListeners = new LinkedHashMap(10); + + private Map>> globalJobListenersMatchers = new LinkedHashMap>>(10); + + private Map>> globalTriggerListenersMatchers = new LinkedHashMap>>(10); + + private ArrayList schedulerListeners = new ArrayList(10); + + + public void addJobListener(JobListener jobListener, Matcher ... matchers) { + addJobListener(jobListener, Arrays.asList(matchers)); + } + + public void addJobListener(JobListener jobListener, List> matchers) { + if (jobListener.getName() == null || jobListener.getName().length() == 0) { + throw new IllegalArgumentException( + "JobListener name cannot be empty."); + } + + synchronized (globalJobListeners) { + globalJobListeners.put(jobListener.getName(), jobListener); + LinkedList> matchersL = new LinkedList>(); + if(matchers != null && matchers.size() > 0) + matchersL.addAll(matchers); + else + matchersL.add(EverythingMatcher.allJobs()); + + globalJobListenersMatchers.put(jobListener.getName(), matchersL); + } + } + + + public void addJobListener(JobListener jobListener) { + addJobListener(jobListener, EverythingMatcher.allJobs()); + } + + public void addJobListener(JobListener jobListener, Matcher matcher) { + if (jobListener.getName() == null || jobListener.getName().length() == 0) { + throw new IllegalArgumentException( + "JobListener name cannot be empty."); + } + + synchronized (globalJobListeners) { + globalJobListeners.put(jobListener.getName(), jobListener); + LinkedList> matchersL = new LinkedList>(); + if(matcher != null) + matchersL.add(matcher); + else + matchersL.add(EverythingMatcher.allJobs()); + + globalJobListenersMatchers.put(jobListener.getName(), matchersL); + } + } + + + public boolean addJobListenerMatcher(String listenerName, Matcher matcher) { + if(matcher == null) + throw new IllegalArgumentException("Null value not acceptable."); + + synchronized (globalJobListeners) { + List> matchers = globalJobListenersMatchers.get(listenerName); + if(matchers == null) + return false; + matchers.add(matcher); + return true; + } + } + + public boolean removeJobListenerMatcher(String listenerName, Matcher matcher) { + if(matcher == null) + throw new IllegalArgumentException("Non-null value not acceptable."); + + synchronized (globalJobListeners) { + List> matchers = globalJobListenersMatchers.get(listenerName); + if(matchers == null) + return false; + return matchers.remove(matcher); + } + } + + public List> getJobListenerMatchers(String listenerName) { + synchronized (globalJobListeners) { + List> matchers = globalJobListenersMatchers.get(listenerName); + if(matchers == null) + return null; + return Collections.unmodifiableList(matchers); + } + } + + public boolean setJobListenerMatchers(String listenerName, List> matchers) { + if(matchers == null) + throw new IllegalArgumentException("Non-null value not acceptable."); + + synchronized (globalJobListeners) { + List> oldMatchers = globalJobListenersMatchers.get(listenerName); + if(oldMatchers == null) + return false; + globalJobListenersMatchers.put(listenerName, matchers); + return true; + } + } + + + public boolean removeJobListener(String name) { + synchronized (globalJobListeners) { + return (globalJobListeners.remove(name) != null); + } + } + + public List getJobListeners() { + synchronized (globalJobListeners) { + return java.util.Collections.unmodifiableList(new LinkedList(globalJobListeners.values())); + } + } + + public JobListener getJobListener(String name) { + synchronized (globalJobListeners) { + return globalJobListeners.get(name); + } + } + + public void addTriggerListener(TriggerListener triggerListener, Matcher ... matchers) { + addTriggerListener(triggerListener, Arrays.asList(matchers)); + } + + public void addTriggerListener(TriggerListener triggerListener, List> matchers) { + if (triggerListener.getName() == null + || triggerListener.getName().length() == 0) { + throw new IllegalArgumentException( + "TriggerListener name cannot be empty."); + } + + synchronized (globalTriggerListeners) { + globalTriggerListeners.put(triggerListener.getName(), triggerListener); + + LinkedList> matchersL = new LinkedList>(); + if(matchers != null && matchers.size() > 0) + matchersL.addAll(matchers); + else + matchersL.add(EverythingMatcher.allTriggers()); + + globalTriggerListenersMatchers.put(triggerListener.getName(), matchersL); + } + } + + public void addTriggerListener(TriggerListener triggerListener) { + addTriggerListener(triggerListener, EverythingMatcher.allTriggers()); + } + + public void addTriggerListener(TriggerListener triggerListener, Matcher matcher) { + if(matcher == null) + throw new IllegalArgumentException("Null value not acceptable for matcher."); + + if (triggerListener.getName() == null + || triggerListener.getName().length() == 0) { + throw new IllegalArgumentException( + "TriggerListener name cannot be empty."); + } + + synchronized (globalTriggerListeners) { + globalTriggerListeners.put(triggerListener.getName(), triggerListener); + List> matchers = new LinkedList>(); + matchers.add(matcher); + globalTriggerListenersMatchers.put(triggerListener.getName(), matchers); + } + } + + public boolean addTriggerListenerMatcher(String listenerName, Matcher matcher) { + if(matcher == null) + throw new IllegalArgumentException("Non-null value not acceptable."); + + synchronized (globalTriggerListeners) { + List> matchers = globalTriggerListenersMatchers.get(listenerName); + if(matchers == null) + return false; + matchers.add(matcher); + return true; + } + } + + public boolean removeTriggerListenerMatcher(String listenerName, Matcher matcher) { + if(matcher == null) + throw new IllegalArgumentException("Non-null value not acceptable."); + + synchronized (globalTriggerListeners) { + List> matchers = globalTriggerListenersMatchers.get(listenerName); + if(matchers == null) + return false; + return matchers.remove(matcher); + } + } + + public List> getTriggerListenerMatchers(String listenerName) { + synchronized (globalTriggerListeners) { + List> matchers = globalTriggerListenersMatchers.get(listenerName); + if(matchers == null) + return null; + return Collections.unmodifiableList(matchers); + } + } + + public boolean setTriggerListenerMatchers(String listenerName, List> matchers) { + if(matchers == null) + throw new IllegalArgumentException("Non-null value not acceptable."); + + synchronized (globalTriggerListeners) { + List> oldMatchers = globalTriggerListenersMatchers.get(listenerName); + if(oldMatchers == null) + return false; + globalTriggerListenersMatchers.put(listenerName, matchers); + return true; + } + } + + public boolean removeTriggerListener(String name) { + synchronized (globalTriggerListeners) { + return (globalTriggerListeners.remove(name) != null); + } + } + + + public List getTriggerListeners() { + synchronized (globalTriggerListeners) { + return java.util.Collections.unmodifiableList(new LinkedList(globalTriggerListeners.values())); + } + } + + public TriggerListener getTriggerListener(String name) { + synchronized (globalTriggerListeners) { + return globalTriggerListeners.get(name); + } + } + + + public void addSchedulerListener(SchedulerListener schedulerListener) { + synchronized (schedulerListeners) { + schedulerListeners.add(schedulerListener); + } + } + + public boolean removeSchedulerListener(SchedulerListener schedulerListener) { + synchronized (schedulerListeners) { + return schedulerListeners.remove(schedulerListener); + } + } + + public List getSchedulerListeners() { + synchronized (schedulerListeners) { + return java.util.Collections.unmodifiableList(new ArrayList(schedulerListeners)); + } + } +} Index: 3rdParty_sources/quartz/org/quartz/core/NullSampledStatisticsImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/NullSampledStatisticsImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/NullSampledStatisticsImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,19 @@ +package org.quartz.core; + +public class NullSampledStatisticsImpl implements SampledStatistics { + public long getJobsCompletedMostRecentSample() { + return 0; + } + + public long getJobsExecutingMostRecentSample() { + return 0; + } + + public long getJobsScheduledMostRecentSample() { + return 0; + } + + public void shutdown() { + // nothing to do + } +} Index: 3rdParty_sources/quartz/org/quartz/core/QuartzScheduler.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/core/QuartzScheduler.java (.../QuartzScheduler.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/core/QuartzScheduler.java (.../QuartzScheduler.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,58 +16,76 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.core; -import java.io.IOException; import java.io.InputStream; +import java.lang.management.ManagementFactory; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import java.util.ArrayList; -import java.util.Collections; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.Set; +import java.util.Timer; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import javax.management.MBeanServer; +import javax.management.ObjectName; + import org.quartz.Calendar; import org.quartz.InterruptableJob; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; +import org.quartz.JobKey; import org.quartz.JobListener; -import org.quartz.JobPersistenceException; +import org.quartz.ListenerManager; +import org.quartz.Matcher; import org.quartz.ObjectAlreadyExistsException; import org.quartz.Scheduler; import org.quartz.SchedulerContext; import org.quartz.SchedulerException; import org.quartz.SchedulerListener; +import org.quartz.SchedulerMetaData; import org.quartz.Trigger; +import static org.quartz.TriggerBuilder.*; +import org.quartz.TriggerKey; import org.quartz.TriggerListener; import org.quartz.UnableToInterruptJobException; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.Trigger.TriggerState; +import org.quartz.core.jmx.QuartzSchedulerMBean; import org.quartz.impl.SchedulerRepository; -import org.quartz.simpl.SimpleJobFactory; +import org.quartz.impl.StdSchedulerFactory; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.listeners.SchedulerListenerSupport; +import org.quartz.simpl.PropertySettingJobFactory; import org.quartz.spi.JobFactory; +import org.quartz.spi.OperableTrigger; import org.quartz.spi.SchedulerPlugin; import org.quartz.spi.SchedulerSignaler; +import org.quartz.spi.ThreadExecutor; +import org.quartz.utils.UpdateChecker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** *

* This is the heart of Quartz, an indirect implementation of the {@link org.quartz.Scheduler} * interface, containing methods to schedule {@link org.quartz.Job}s, * register {@link org.quartz.JobListener} instances, etc. - *

// TODO: more docs... + *

* * @see org.quartz.Scheduler * @see org.quartz.core.QuartzSchedulerThread @@ -92,17 +110,32 @@ static { Properties props = new Properties(); + InputStream is = null; try { - InputStream is = - QuartzScheduler.class.getResourceAsStream("/build.properties"); + is = QuartzScheduler.class.getResourceAsStream("quartz-build.properties"); if(is != null) { props.load(is); - VERSION_MAJOR = props.getProperty("version.major"); - VERSION_MINOR = props.getProperty("version.minor"); - VERSION_ITERATION = props.getProperty("version.iter"); + String version = props.getProperty("version"); + if (version != null) { + String[] versionComponents = version.split("\\."); + VERSION_MAJOR = versionComponents[0]; + VERSION_MINOR = versionComponents[1]; + if(versionComponents.length > 2) + VERSION_ITERATION = versionComponents[2]; + else + VERSION_ITERATION = "0"; + } else { + (LoggerFactory.getLogger(QuartzScheduler.class)).error( + "Can't parse Quartz version from quartz-build.properties"); + } } - } catch (IOException e) { - getLog().error("Error loading version info from build.properties.", e); + } catch (Exception e) { + (LoggerFactory.getLogger(QuartzScheduler.class)).error( + "Error loading version info from quartz-build.properties.", e); + } finally { + if(is != null) { + try { is.close(); } catch(Exception ignore) {} + } } } @@ -123,19 +156,15 @@ private SchedulerContext context = new SchedulerContext(); - private HashMap jobListeners = new HashMap(10); + private ListenerManager listenerManager = new ListenerManagerImpl(); + + private HashMap internalJobListeners = new HashMap(10); - private ArrayList globalJobListeners = new ArrayList(10); + private HashMap internalTriggerListeners = new HashMap(10); - private HashMap triggerListeners = new HashMap(10); + private ArrayList internalSchedulerListeners = new ArrayList(10); - private ArrayList globalTriggerListeners = new ArrayList(10); - - private ArrayList schedulerListeners = new ArrayList(10); - - private ArrayList schedulerPlugins = new ArrayList(10); - - private JobFactory jobFactory = new SimpleJobFactory(); + private JobFactory jobFactory = new PropertySettingJobFactory(); ExecutingJobsManager jobMgr = null; @@ -145,14 +174,27 @@ private Random random = new Random(); - private ArrayList holdToPreventGC = new ArrayList(5); + private ArrayList holdToPreventGC = new ArrayList(5); private boolean signalOnSchedulingChange = true; - private boolean closed = false; + private volatile boolean closed = false; + private volatile boolean shuttingDown = false; + private boolean boundRemotely = false; + private QuartzSchedulerMBean jmxBean = null; + private Date initialStart = null; + + /** Update timer that must be cancelled upon shutdown. */ + private final Timer updateTimer; + private final Logger log = LoggerFactory.getLogger(getClass()); + + // private static final Map MGMT_SVR_BY_BIND = new + // HashMap(); + // private String registeredManagementServerBind; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -169,32 +211,106 @@ * * @see QuartzSchedulerResources */ - public QuartzScheduler(QuartzSchedulerResources resources, - SchedulingContext ctxt, long idleWaitTime, long dbRetryInterval) - throws SchedulerException { + public QuartzScheduler(QuartzSchedulerResources resources, long idleWaitTime, @Deprecated long dbRetryInterval) + throws SchedulerException { this.resources = resources; - try { - bind(); - } catch (Exception re) { - throw new SchedulerException( - "Unable to bind scheduler to RMI Registry.", re); + if (resources.getJobStore() instanceof JobListener) { + addInternalJobListener((JobListener)resources.getJobStore()); } - this.schedThread = new QuartzSchedulerThread(this, resources, ctxt); - if (idleWaitTime > 0) this.schedThread.setIdleWaitTime(idleWaitTime); - if (dbRetryInterval > 0) - this.schedThread.setDbFailureRetryInterval(dbRetryInterval); + this.schedThread = new QuartzSchedulerThread(this, resources); + ThreadExecutor schedThreadExecutor = resources.getThreadExecutor(); + schedThreadExecutor.execute(this.schedThread); + if (idleWaitTime > 0) { + this.schedThread.setIdleWaitTime(idleWaitTime); + } jobMgr = new ExecutingJobsManager(); - addGlobalJobListener(jobMgr); + addInternalJobListener(jobMgr); errLogger = new ErrorLogger(); - addSchedulerListener(errLogger); + addInternalSchedulerListener(errLogger); - signaler = new SchedulerSignalerImpl(this); + signaler = new SchedulerSignalerImpl(this, this.schedThread); + if(shouldRunUpdateCheck()) + updateTimer = scheduleUpdateCheck(); + else + updateTimer = null; + getLog().info("Quartz Scheduler v." + getVersion() + " created."); } + public void initialize() throws SchedulerException { + + try { + bind(); + } catch (Exception re) { + throw new SchedulerException( + "Unable to bind scheduler to RMI Registry.", re); + } + + if (resources.getJMXExport()) { + try { + registerJMX(); + } catch (Exception e) { + throw new SchedulerException( + "Unable to register scheduler with MBeanServer.", e); + } + } + + // ManagementRESTServiceConfiguration managementRESTServiceConfiguration + // = resources.getManagementRESTServiceConfiguration(); + // + // if (managementRESTServiceConfiguration != null && + // managementRESTServiceConfiguration.isEnabled()) { + // try { + // /** + // * ManagementServer will only be instantiated and started if one + // * isn't already running on the configured port for this class + // * loader space. + // */ + // synchronized (QuartzScheduler.class) { + // if + // (!MGMT_SVR_BY_BIND.containsKey(managementRESTServiceConfiguration.getBind())) + // { + // Class managementServerImplClass = + // Class.forName("org.quartz.management.ManagementServerImpl"); + // Class managementRESTServiceConfigurationClass[] = new Class[] { + // managementRESTServiceConfiguration.getClass() }; + // Constructor managementRESTServiceConfigurationConstructor = + // managementServerImplClass + // .getConstructor(managementRESTServiceConfigurationClass); + // Object arglist[] = new Object[] { managementRESTServiceConfiguration + // }; + // ManagementServer embeddedRESTServer = ((ManagementServer) + // managementRESTServiceConfigurationConstructor.newInstance(arglist)); + // embeddedRESTServer.start(); + // MGMT_SVR_BY_BIND.put(managementRESTServiceConfiguration.getBind(), + // embeddedRESTServer); + // } + // registeredManagementServerBind = + // managementRESTServiceConfiguration.getBind(); + // ManagementServer embeddedRESTServer = + // MGMT_SVR_BY_BIND.get(registeredManagementServerBind); + // embeddedRESTServer.register(this); + // } + // } catch (Exception e) { + // throw new + // SchedulerException("Unable to start the scheduler management REST service", + // e); + // } + // } + + + getLog().info("Scheduler meta-data: " + + (new SchedulerMetaData(getSchedulerName(), + getSchedulerInstanceId(), getClass(), boundRemotely, runningSince() != null, + isInStandbyMode(), isShutdown(), runningSince(), + numJobsExecuted(), getJobStoreClass(), + supportsPersistence(), isClustered(), getThreadPoolClass(), + getThreadPoolSize(), getVersion())).toString()); + } + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -211,6 +327,14 @@ public static String getVersionMajor() { return VERSION_MAJOR; } + + private boolean shouldRunUpdateCheck() { + if(resources.isRunUpdateCheck() && !Boolean.getBoolean(StdSchedulerFactory.PROP_SCHED_SKIP_UPDATE_CHECK) && + !Boolean.getBoolean("org.terracotta.quartz.skipUpdateCheck")) { + return true; + } + return false; + } public static String getVersionMinor() { return VERSION_MINOR; @@ -224,28 +348,61 @@ return signaler; } - public static Log getLog() { - return LogFactory.getLog(QuartzScheduler.class); + public Logger getLog() { + return log; } + + /** + * Update checker scheduler - fires every week + */ + private Timer scheduleUpdateCheck() { + Timer rval = new Timer(true); + rval.scheduleAtFixedRate(new UpdateChecker(), 1000, 7 * 24 * 60 * 60 * 1000L); + return rval; + } /** + * Register the scheduler in the local MBeanServer. + */ + private void registerJMX() throws Exception { + String jmxObjectName = resources.getJMXObjectName(); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + jmxBean = new QuartzSchedulerMBeanImpl(this); + mbs.registerMBean(jmxBean, new ObjectName(jmxObjectName)); + } + + /** + * Unregister the scheduler from the local MBeanServer. + */ + private void unregisterJMX() throws Exception { + String jmxObjectName = resources.getJMXObjectName(); + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + mbs.unregisterMBean(new ObjectName(jmxObjectName)); + jmxBean.setSampledStatisticsEnabled(false); + getLog().info("Scheduler unregistered from name '" + jmxObjectName + "' in the local MBeanServer."); + } + + /** *

* 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; + if (host == null || host.length() == 0) { + return; + } RemotableQuartzScheduler exportable = null; - if(resources.getRMIServerPort() > 0) + if(resources.getRMIServerPort() > 0) { exportable = (RemotableQuartzScheduler) UnicastRemoteObject .exportObject(this, resources.getRMIServerPort()); - else + } else { exportable = (RemotableQuartzScheduler) UnicastRemoteObject .exportObject(this); + } Registry registry = null; @@ -283,9 +440,13 @@ .getRMIRegistryHost(), resources.getRMIRegistryPort()); } - registry.rebind(resources.getUniqueIdentifier(), exportable); + String bindName = resources.getRMIBindName(); + + registry.rebind(bindName, exportable); + + boundRemotely = true; - getLog().info("Scheduler bound to RMI registry."); + getLog().info("Scheduler bound to RMI registry under name '" + bindName + "'"); } /** @@ -296,18 +457,22 @@ 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; + if (host == null || host.length() == 0) { + return; + } Registry registry = LocateRegistry.getRegistry(resources .getRMIRegistryHost(), resources.getRMIRegistryPort()); + String bindName = resources.getRMIBindName(); + try { - registry.unbind(resources.getUniqueIdentifier()); + registry.unbind(bindName); UnicastRemoteObject.unexportObject(this, true); } catch (java.rmi.NotBoundException nbe) { } - getLog().info("Scheduler un-bound from RMI registry."); + getLog().info("Scheduler un-bound from name '" + bindName + "' in RMI registry"); } /** @@ -330,13 +495,16 @@ /** *

- * Returns the name of the QuartzScheduler. + * Returns the name of the thread group for Quartz's main threads. *

*/ public ThreadGroup getSchedulerThreadGroup() { if (threadGroup == null) { threadGroup = new ThreadGroup("QuartzScheduler:" + getSchedulerName()); + if (resources.getMakeSchedulerThreadDaemon()) { + threadGroup.setDaemon(true); + } } return threadGroup; @@ -369,7 +537,7 @@ /////////////////////////////////////////////////////////////////////////// /// - /// Schedululer State Management Methods + /// Scheduler State Management Methods /// /////////////////////////////////////////////////////////////////////////// @@ -385,22 +553,51 @@ */ public void start() throws SchedulerException { - if (closed) - throw new SchedulerException( - "The Scheduler cannot be restarted after shutdown() has been called."); + if (shuttingDown|| closed) { + throw new SchedulerException( + "The Scheduler cannot be restarted after shutdown() has been called."); + } - schedThread.togglePause(false); + // QTZ-212 : calling new schedulerStarting() method on the listeners + // right after entering start() + notifySchedulerListenersStarting(); if (initialStart == null) { initialStart = new Date(); this.resources.getJobStore().schedulerStarted(); startPlugins(); + } else { + resources.getJobStore().schedulerResumed(); } + schedThread.togglePause(false); + getLog().info( "Scheduler " + resources.getUniqueIdentifier() + " started."); + + notifySchedulerListenersStarted(); } + public void startDelayed(final int seconds) throws SchedulerException + { + if (shuttingDown || closed) { + throw new SchedulerException( + "The Scheduler cannot be restarted after shutdown() has been called."); + } + + Thread t = new Thread(new Runnable() { + public void run() { + try { Thread.sleep(seconds * 1000L); } + catch(InterruptedException ignore) {} + try { start(); } + catch(SchedulerException se) { + getLog().error("Unable to start secheduler after startup delay.", se); + } + } + }); + t.start(); + } + /** *

* Temporarily halts the QuartzScheduler's firing of {@link org.quartz.Trigger}s. @@ -411,9 +608,11 @@ *

*/ public void standby() { + resources.getJobStore().schedulerPaused(); schedThread.togglePause(true); getLog().info( "Scheduler " + resources.getUniqueIdentifier() + " paused."); + notifySchedulerListenersInStandbyMode(); } /** @@ -426,22 +625,28 @@ } public Date runningSince() { - return initialStart; + if(initialStart == null) + return null; + return new Date(initialStart.getTime()); } public int numJobsExecuted() { return jobMgr.getNumJobsFired(); } - public Class getJobStoreClass() { + public Class getJobStoreClass() { return resources.getJobStore().getClass(); } public boolean supportsPersistence() { return resources.getJobStore().supportsPersistence(); } - public Class getThreadPoolClass() { + public boolean isClustered() { + return resources.getJobStore().isClustered(); + } + + public Class getThreadPoolClass() { return resources.getThreadPool().getClass(); } @@ -480,43 +685,89 @@ */ public void shutdown(boolean waitForJobsToComplete) { - if(closed == true) + if(shuttingDown || closed) { return; + } + shuttingDown = true; + getLog().info( "Scheduler " + resources.getUniqueIdentifier() + " shutting down."); + // boolean removeMgmtSvr = false; + // if (registeredManagementServerBind != null) { + // ManagementServer standaloneRestServer = + // MGMT_SVR_BY_BIND.get(registeredManagementServerBind); + // + // try { + // standaloneRestServer.unregister(this); + // + // if (!standaloneRestServer.hasRegistered()) { + // removeMgmtSvr = true; + // standaloneRestServer.stop(); + // } + // } catch (Exception e) { + // getLog().warn("Failed to shutdown the ManagementRESTService", e); + // } finally { + // if (removeMgmtSvr) { + // MGMT_SVR_BY_BIND.remove(registeredManagementServerBind); + // } + // + // registeredManagementServerBind = null; + // } + // } + standby(); + schedThread.halt(waitForJobsToComplete); + + notifySchedulerListenersShuttingdown(); + + if( (resources.isInterruptJobsOnShutdown() && !waitForJobsToComplete) || + (resources.isInterruptJobsOnShutdownWithWait() && waitForJobsToComplete)) { + List jobs = getCurrentlyExecutingJobs(); + for(JobExecutionContext job: jobs) { + if(job.getJobInstance() instanceof InterruptableJob) + try { + ((InterruptableJob)job.getJobInstance()).interrupt(); + } catch (Throwable e) { + // do nothing, this was just a courtesy effort + getLog().warn("Encountered error when interrupting job {} during shutdown: {}", job.getJobDetail().getKey(), e); + } + } + } + + resources.getThreadPool().shutdown(waitForJobsToComplete); + closed = true; - schedThread.halt(); + if (resources.getJMXExport()) { + try { + unregisterJMX(); + } catch (Exception e) { + } + } - resources.getThreadPool().shutdown(waitForJobsToComplete); - - if (waitForJobsToComplete) { - while (jobMgr.getNumJobsCurrentlyExecuting() > 0) - try { - Thread.sleep(100); - } catch (Exception ignore) { - } + if(boundRemotely) { + try { + unBind(); + } catch (RemoteException re) { + } } + + shutdownPlugins(); resources.getJobStore().shutdown(); notifySchedulerListenersShutdown(); - shutdownPlugins(); - SchedulerRepository.getInstance().remove(resources.getName()); holdToPreventGC.clear(); - try { - unBind(); - } catch (RemoteException re) { - } - + if(updateTimer != null) + updateTimer.cancel(); + getLog().info( "Scheduler " + resources.getUniqueIdentifier() + " shutdown complete."); @@ -531,25 +782,40 @@ return closed; } + public boolean isShuttingDown() { + return shuttingDown; + } + + public boolean isStarted() { + return !shuttingDown && !closed && !isInStandbyMode() && initialStart != null; + } + public void validateState() throws SchedulerException { - if (isShutdown()) - throw new SchedulerException("The Scheduler has been shutdown."); + if (isShutdown()) { + throw new SchedulerException("The Scheduler has been shutdown."); + } // other conditions to check (?) } /** *

* Return a list of JobExecutionContext objects that - * represent all currently executing Jobs. + * 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() { + public List getCurrentlyExecutingJobs() { return jobMgr.getExecutingJobs(); } @@ -575,43 +841,51 @@ * if the Job or Trigger cannot be added to the Scheduler, or * there is an internal Scheduler error. */ - public Date scheduleJob(SchedulingContext ctxt, JobDetail jobDetail, + public Date scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException { validateState(); - jobDetail.validate(); + if (jobDetail == null) { + throw new SchedulerException("JobDetail cannot be null"); + } + + if (trigger == null) { + throw new SchedulerException("Trigger cannot be null"); + } + + if (jobDetail.getKey() == null) { + throw new SchedulerException("Job's key cannot be null"); + } - if (trigger.getJobName() == null) { - trigger.setJobName(jobDetail.getName()); - trigger.setJobGroup(jobDetail.getGroup()); - } else if (trigger.getJobName() != null - && !trigger.getJobName().equals(jobDetail.getName())) { + if (jobDetail.getJobClass() == null) { + throw new SchedulerException("Job's class cannot be null"); + } + + OperableTrigger trig = (OperableTrigger)trigger; + + if (trigger.getJobKey() == null) { + trig.setJobKey(jobDetail.getKey()); + } else if (!trigger.getJobKey().equals(jobDetail.getKey())) { throw new SchedulerException( - "Trigger does not reference given job!", - SchedulerException.ERR_CLIENT_ERROR); - } else if (trigger.getJobGroup() != null - && !trigger.getJobGroup().equals(jobDetail.getGroup())) { - throw new SchedulerException( - "Trigger does not reference given job!", - SchedulerException.ERR_CLIENT_ERROR); + "Trigger does not reference given job!"); } - trigger.validate(); + trig.validate(); Calendar cal = null; if (trigger.getCalendarName() != null) { - cal = resources.getJobStore().retrieveCalendar(ctxt, - trigger.getCalendarName()); + cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName()); } - Date ft = trigger.computeFirstFireTime(cal); + Date ft = trig.computeFirstFireTime(cal); - if (ft == null) - throw new SchedulerException( - "Based on configured schedule, the given trigger will never fire.", - SchedulerException.ERR_CLIENT_ERROR); + if (ft == null) { + throw new SchedulerException( + "Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire."); + } - resources.getJobStore().storeJobAndTrigger(ctxt, jobDetail, trigger); - notifySchedulerThread(); + resources.getJobStore().storeJobAndTrigger(jobDetail, trig); + notifySchedulerListenersJobAdded(jobDetail); + notifySchedulerThread(trigger.getNextFireTime().getTime()); notifySchedulerListenersSchduled(trigger); return ft; @@ -628,30 +902,35 @@ * added to the Scheduler, or there is an internal Scheduler * error. */ - public Date scheduleJob(SchedulingContext ctxt, Trigger trigger) - throws SchedulerException { + public Date scheduleJob(Trigger trigger) + throws SchedulerException { validateState(); - trigger.validate(); + if (trigger == null) { + throw new SchedulerException("Trigger cannot be null"); + } + OperableTrigger trig = (OperableTrigger)trigger; + + trig.validate(); + Calendar cal = null; if (trigger.getCalendarName() != null) { - cal = resources.getJobStore().retrieveCalendar(ctxt, - trigger.getCalendarName()); - if(cal == null) + cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName()); + if(cal == null) { throw new SchedulerException( - "Calendar not found: " + trigger.getCalendarName(), - SchedulerException.ERR_PERSISTENCE_CALENDAR_DOES_NOT_EXIST); + "Calendar not found: " + trigger.getCalendarName()); + } } - Date ft = trigger.computeFirstFireTime(cal); + Date ft = trig.computeFirstFireTime(cal); - if (ft == null) - throw new SchedulerException( - "Based on configured schedule, the given trigger will never fire.", - SchedulerException.ERR_CLIENT_ERROR); + if (ft == null) { + throw new SchedulerException( + "Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire."); + } - resources.getJobStore().storeTrigger(ctxt, trigger, false); - notifySchedulerThread(); + resources.getJobStore().storeTrigger(trig, false); + notifySchedulerThread(trigger.getNextFireTime().getTime()); notifySchedulerListenersSchduled(trigger); return ft; @@ -675,16 +954,21 @@ * durable, or a Job with the same name already exists, and * replace is false. */ - public void addJob(SchedulingContext ctxt, JobDetail jobDetail, - boolean replace) throws SchedulerException { + public void addJob(JobDetail jobDetail, boolean replace) throws SchedulerException { + addJob(jobDetail, replace, false); + } + + public void addJob(JobDetail jobDetail, boolean replace, boolean storeNonDurableWhileAwaitingScheduling) throws SchedulerException { validateState(); - if (!jobDetail.isDurable() && !replace) - throw new SchedulerException( - "Jobs added with no trigger must be durable.", - SchedulerException.ERR_CLIENT_ERROR); + if (!storeNonDurableWhileAwaitingScheduling && !jobDetail.isDurable()) { + throw new SchedulerException( + "Jobs added with no trigger must be durable."); + } - resources.getJobStore().storeJob(ctxt, jobDetail, replace); + resources.getJobStore().storeJob(jobDetail, replace); + notifySchedulerThread(0L); + notifySchedulerListenersJobAdded(jobDetail); } /** @@ -697,34 +981,118 @@ * @throws SchedulerException * if there is an internal Scheduler error. */ - public boolean deleteJob(SchedulingContext ctxt, String jobName, - String groupName) throws SchedulerException { + public boolean deleteJob(JobKey jobKey) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; + boolean result = false; - return resources.getJobStore().removeJob(ctxt, jobName, groupName); + List triggers = getTriggersOfJob(jobKey); + for (Trigger trigger : triggers) { + if (!unscheduleJob(trigger.getKey())) { + StringBuilder sb = new StringBuilder().append( + "Unable to unschedule trigger [").append( + trigger.getKey()).append("] while deleting job [") + .append(jobKey).append( + "]"); + throw new SchedulerException(sb.toString()); + } + result = true; + } + + result = resources.getJobStore().removeJob(jobKey) || result; + if (result) { + notifySchedulerThread(0L); + notifySchedulerListenersJobDeleted(jobKey); + } + return result; } + public boolean deleteJobs(List jobKeys) throws SchedulerException { + validateState(); + + boolean result = false; + + result = resources.getJobStore().removeJobs(jobKeys); + notifySchedulerThread(0L); + for(JobKey key: jobKeys) + notifySchedulerListenersJobDeleted(key); + return result; + } + + public void scheduleJobs(Map> triggersAndJobs, boolean replace) throws SchedulerException { + validateState(); + + // make sure all triggers refer to their associated job + for(Entry> e: triggersAndJobs.entrySet()) { + JobDetail job = e.getKey(); + if(job == null) // there can be one of these (for adding a bulk set of triggers for pre-existing jobs) + continue; + Set triggers = e.getValue(); + if(triggers == null) // this is possible because the job may be durable, and not yet be having triggers + continue; + for(Trigger trigger: triggers) { + OperableTrigger opt = (OperableTrigger)trigger; + opt.setJobKey(job.getKey()); + + opt.validate(); + + Calendar cal = null; + if (trigger.getCalendarName() != null) { + cal = resources.getJobStore().retrieveCalendar(trigger.getCalendarName()); + if(cal == null) { + throw new SchedulerException( + "Calendar '" + trigger.getCalendarName() + "' not found for trigger: " + trigger.getKey()); + } + } + Date ft = opt.computeFirstFireTime(cal); + + if (ft == null) { + throw new SchedulerException( + "Based on configured schedule, the given trigger will never fire."); + } + } + } + + resources.getJobStore().storeJobsAndTriggers(triggersAndJobs, replace); + notifySchedulerThread(0L); + for(JobDetail job: triggersAndJobs.keySet()) + notifySchedulerListenersJobAdded(job); + } + + public void scheduleJob(JobDetail jobDetail, Set triggersForJob, + boolean replace) throws SchedulerException { + Map> triggersAndJobs = new HashMap>(); + triggersAndJobs.put(jobDetail, triggersForJob); + scheduleJobs(triggersAndJobs, replace); + } + + public boolean unscheduleJobs(List triggerKeys) throws SchedulerException { + validateState(); + + boolean result = false; + + result = resources.getJobStore().removeTriggers(triggerKeys); + notifySchedulerThread(0L); + for(TriggerKey key: triggerKeys) + notifySchedulerListenersUnscheduled(key); + return result; + } + /** *

* Remove the indicated {@link org.quartz.Trigger} from the * scheduler. *

*/ - public boolean unscheduleJob(SchedulingContext ctxt, String triggerName, - String groupName) throws SchedulerException { + public boolean unscheduleJob(TriggerKey triggerKey) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - if (resources.getJobStore().removeTrigger(ctxt, triggerName, groupName)) { - notifySchedulerThread(); - notifySchedulerListenersUnschduled(triggerName, groupName); - } else + if (resources.getJobStore().removeTrigger(triggerKey)) { + notifySchedulerThread(0L); + notifySchedulerListenersUnscheduled(triggerKey); + } else { return false; + } return true; } @@ -736,44 +1104,52 @@ * given name, and store the new given one - which must be associated * with the same job. *

- * - * @param triggerName - * The name of the 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 { + public Date rescheduleJob(TriggerKey triggerKey, + Trigger newTrigger) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; + if (triggerKey == null) { + throw new IllegalArgumentException("triggerKey cannot be null"); + } + if (newTrigger == null) { + throw new IllegalArgumentException("newTrigger cannot be null"); + } - newTrigger.validate(); + OperableTrigger trig = (OperableTrigger)newTrigger; + Trigger oldTrigger = getTrigger(triggerKey); + if (oldTrigger == null) { + return null; + } else { + trig.setJobKey(oldTrigger.getJobKey()); + } + trig.validate(); Calendar cal = null; if (newTrigger.getCalendarName() != null) { - cal = resources.getJobStore().retrieveCalendar(ctxt, + cal = resources.getJobStore().retrieveCalendar( newTrigger.getCalendarName()); } - Date ft = newTrigger.computeFirstFireTime(cal); + Date ft = trig.computeFirstFireTime(cal); - if (ft == null) + if (ft == null) { throw new SchedulerException( - "Based on configured schedule, the given trigger will never fire.", - SchedulerException.ERR_CLIENT_ERROR); + "Based on configured schedule, the given trigger will never fire."); + } - if (resources.getJobStore().replaceTrigger(ctxt, triggerName, groupName, newTrigger)) { - notifySchedulerThread(); - notifySchedulerListenersUnschduled(triggerName, groupName); + if (resources.getJobStore().replaceTrigger(triggerKey, trig)) { + notifySchedulerThread(newTrigger.getNextFireTime().getTime()); + notifySchedulerListenersUnscheduled(triggerKey); notifySchedulerListenersSchduled(newTrigger); - } else + } else { return null; + } return ft; @@ -782,7 +1158,9 @@ private String newTriggerId() { long r = random.nextLong(); - if (r < 0) r = -r; + if (r < 0) { + r = -r; + } return "MT_" + Long.toString(r, 30 + (int) (System.currentTimeMillis() % 7)); } @@ -793,104 +1171,87 @@ * now) - with a non-volatile trigger. *

*/ - public void triggerJob(SchedulingContext ctxt, String jobName, - String groupName, JobDataMap data) throws SchedulerException { + @SuppressWarnings("deprecation") + public void triggerJob(JobKey jobKey, JobDataMap data) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - Trigger trig = new org.quartz.SimpleTrigger(newTriggerId(), - Scheduler.DEFAULT_MANUAL_TRIGGERS, jobName, groupName, - new Date(), null, 0, 0); - trig.setVolatility(false); + OperableTrigger trig = (OperableTrigger) newTrigger().withIdentity(newTriggerId(), Scheduler.DEFAULT_GROUP).forJob(jobKey).build(); trig.computeFirstFireTime(null); - if(data != null) + if(data != null) { trig.setJobDataMap(data); + } boolean collision = true; while (collision) { try { - resources.getJobStore().storeTrigger(ctxt, trig, false); + resources.getJobStore().storeTrigger(trig, false); collision = false; } catch (ObjectAlreadyExistsException oaee) { - trig.setName(newTriggerId()); + trig.setKey(new TriggerKey(newTriggerId(), Scheduler.DEFAULT_GROUP)); } } - notifySchedulerThread(); + notifySchedulerThread(trig.getNextFireTime().getTime()); notifySchedulerListenersSchduled(trig); } /** *

- * Trigger the identified {@link org.quartz.Job} (execute it - * now) - with a volatile trigger. + * Store and schedule the identified {@link org.quartz.spi.OperableTrigger} *

*/ - public void triggerJobWithVolatileTrigger(SchedulingContext ctxt, - String jobName, String groupName, JobDataMap data) throws SchedulerException { + public void triggerJob(OperableTrigger trig) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - Trigger trig = new org.quartz.SimpleTrigger(newTriggerId(), - Scheduler.DEFAULT_MANUAL_TRIGGERS, jobName, groupName, - new Date(), null, 0, 0); - trig.setVolatility(true); trig.computeFirstFireTime(null); - if(data != null) - trig.setJobDataMap(data); - + boolean collision = true; while (collision) { try { - resources.getJobStore().storeTrigger(ctxt, trig, false); + resources.getJobStore().storeTrigger(trig, false); collision = false; } catch (ObjectAlreadyExistsException oaee) { - trig.setName(newTriggerId()); + trig.setKey(new TriggerKey(newTriggerId(), Scheduler.DEFAULT_GROUP)); } } - notifySchedulerThread(); + notifySchedulerThread(trig.getNextFireTime().getTime()); notifySchedulerListenersSchduled(trig); } - + /** *

* Pause the {@link Trigger} with the given name. *

* */ - public void pauseTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws SchedulerException { + public void pauseTrigger(TriggerKey triggerKey) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - resources.getJobStore().pauseTrigger(ctxt, triggerName, groupName); - notifySchedulerThread(); - notifySchedulerListenersPausedTrigger(triggerName, groupName); + resources.getJobStore().pauseTrigger(triggerKey); + notifySchedulerThread(0L); + notifySchedulerListenersPausedTrigger(triggerKey); } /** *

- * Pause all of the {@link Trigger}s in the given group. + * Pause all of the {@link Trigger}s in the matching groups. *

* */ - public void pauseTriggerGroup(SchedulingContext ctxt, String groupName) - throws SchedulerException { + public void pauseTriggers(GroupMatcher matcher) + throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - resources.getJobStore().pauseTriggerGroup(ctxt, groupName); - notifySchedulerThread(); - notifySchedulerListenersPausedTrigger(null, groupName); + if(matcher == null) { + matcher = GroupMatcher.groupEquals(Scheduler.DEFAULT_GROUP); + } + + Collection pausedGroups = resources.getJobStore().pauseTriggers(matcher); + notifySchedulerThread(0L); + for (String pausedGroup : pausedGroups) { + notifySchedulerListenersPausedTriggers(pausedGroup); + } } /** @@ -900,35 +1261,34 @@ *

* */ - public void pauseJob(SchedulingContext ctxt, String jobName, - String groupName) throws SchedulerException { + public void pauseJob(JobKey jobKey) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - resources.getJobStore().pauseJob(ctxt, jobName, groupName); - notifySchedulerThread(); - notifySchedulerListenersPausedJob(jobName, groupName); + resources.getJobStore().pauseJob(jobKey); + notifySchedulerThread(0L); + notifySchedulerListenersPausedJob(jobKey); } /** *

* Pause all of the {@link org.quartz.JobDetail}s in the - * given group - by pausing all of their Triggers. + * matching groups - by pausing all of their Triggers. *

* */ - public void pauseJobGroup(SchedulingContext ctxt, String groupName) - throws SchedulerException { + public void pauseJobs(GroupMatcher groupMatcher) + throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; + if(groupMatcher == null) { + groupMatcher = GroupMatcher.groupEquals(Scheduler.DEFAULT_GROUP); + } - resources.getJobStore().pauseJobGroup(ctxt, groupName); - notifySchedulerThread(); - notifySchedulerListenersPausedJob(null, groupName); + Collection pausedGroups = resources.getJobStore().pauseJobs(groupMatcher); + notifySchedulerThread(0L); + for (String pausedGroup : pausedGroups) { + notifySchedulerListenersPausedJobs(pausedGroup); + } } /** @@ -943,22 +1303,18 @@ *

* */ - public void resumeTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws SchedulerException { + public void resumeTrigger(TriggerKey triggerKey) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - resources.getJobStore().resumeTrigger(ctxt, triggerName, groupName); - notifySchedulerThread(); - notifySchedulerListenersResumedTrigger(triggerName, groupName); + resources.getJobStore().resumeTrigger(triggerKey); + notifySchedulerThread(0L); + notifySchedulerListenersResumedTrigger(triggerKey); } /** *

* Resume (un-pause) all of the {@link Trigger}s in the - * given group. + * matching groups. *

* *

@@ -967,20 +1323,23 @@ *

* */ - public void resumeTriggerGroup(SchedulingContext ctxt, String groupName) - throws SchedulerException { + public void resumeTriggers(GroupMatcher matcher) + throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - resources.getJobStore().resumeTriggerGroup(ctxt, groupName); - notifySchedulerThread(); - notifySchedulerListenersResumedTrigger(null, groupName); + if(matcher == null) { + matcher = GroupMatcher.groupEquals(Scheduler.DEFAULT_GROUP); + } + + Collection pausedGroups = resources.getJobStore().resumeTriggers(matcher); + notifySchedulerThread(0L); + for (String pausedGroup : pausedGroups) { + notifySchedulerListenersResumedTriggers(pausedGroup); + } } - public Set getPausedTriggerGroups(SchedulingContext ctxt) throws SchedulerException { - return resources.getJobStore().getPausedTriggerGroups(ctxt); + public Set getPausedTriggerGroups() throws SchedulerException { + return resources.getJobStore().getPausedTriggerGroups(); } /** @@ -996,22 +1355,18 @@ *

* */ - public void resumeJob(SchedulingContext ctxt, String jobName, - String groupName) throws SchedulerException { + public void resumeJob(JobKey jobKey) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - resources.getJobStore().resumeJob(ctxt, jobName, groupName); - notifySchedulerThread(); - notifySchedulerListenersResumedJob(jobName, groupName); + resources.getJobStore().resumeJob(jobKey); + notifySchedulerThread(0L); + notifySchedulerListenersResumedJob(jobKey); } /** *

* Resume (un-pause) all of the {@link org.quartz.JobDetail}s - * in the given group. + * in the matching groups. *

* *

@@ -1021,39 +1376,42 @@ *

* */ - public void resumeJobGroup(SchedulingContext ctxt, String groupName) - throws SchedulerException { + public void resumeJobs(GroupMatcher matcher) + throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; + if(matcher == null) { + matcher = GroupMatcher.groupEquals(Scheduler.DEFAULT_GROUP); + } - resources.getJobStore().resumeJobGroup(ctxt, groupName); - notifySchedulerThread(); - notifySchedulerListenersResumedJob(null, groupName); + Collection resumedGroups = resources.getJobStore().resumeJobs(matcher); + notifySchedulerThread(0L); + for (String pausedGroup : resumedGroups) { + notifySchedulerListenersResumedJobs(pausedGroup); + } } /** *

- * Pause all triggers - equivalent of calling pauseTriggerGroup(group) - * on every group. + * Pause all triggers - equivalent of calling pauseTriggers(GroupMatcher) + * with a matcher matching all known groups. *

* *

* When resumeAll() is called (to un-pause), trigger misfire * instructions WILL be applied. *

* - * @see #resumeAll(SchedulingContext) - * @see #pauseTriggerGroup(SchedulingContext, String) - * @see #pause() + * @see #resumeAll() + * @see #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) + * @see #standby() */ - public void pauseAll(SchedulingContext ctxt) throws SchedulerException { + public void pauseAll() throws SchedulerException { validateState(); - resources.getJobStore().pauseAll(ctxt); - notifySchedulerThread(); - notifySchedulerListenersPausedTrigger(null, null); + resources.getJobStore().pauseAll(); + notifySchedulerThread(0L); + notifySchedulerListenersPausedTriggers(null); } /** @@ -1067,42 +1425,43 @@ * Trigger's misfire instruction will be applied. *

* - * @see #pauseAll(SchedulingContext) + * @see #pauseAll() */ - public void resumeAll(SchedulingContext ctxt) throws SchedulerException { + public void resumeAll() throws SchedulerException { validateState(); - resources.getJobStore().resumeAll(ctxt); - notifySchedulerThread(); - notifySchedulerListenersResumedTrigger(null, null); + resources.getJobStore().resumeAll(); + notifySchedulerThread(0L); + notifySchedulerListenersResumedTrigger(null); } /** *

* Get the names of all known {@link org.quartz.Job} groups. *

*/ - public String[] getJobGroupNames(SchedulingContext ctxt) - throws SchedulerException { + public List getJobGroupNames() + throws SchedulerException { validateState(); - return resources.getJobStore().getJobGroupNames(ctxt); + return resources.getJobStore().getJobGroupNames(); } /** *

* Get the names of all the {@link org.quartz.Job}s in the - * given group. + * matching groups. *

*/ - public String[] getJobNames(SchedulingContext ctxt, String groupName) - throws SchedulerException { + public Set getJobKeys(GroupMatcher matcher) + throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; + if(matcher == null) { + matcher = GroupMatcher.groupEquals(Scheduler.DEFAULT_GROUP); + } - return resources.getJobStore().getJobNames(ctxt, groupName); + return resources.getJobStore().getJobKeys(matcher); } /** @@ -1111,15 +1470,10 @@ * identified {@link org.quartz.JobDetail}. *

*/ - public Trigger[] getTriggersOfJob(SchedulingContext ctxt, String jobName, - String groupName) throws SchedulerException { + public List getTriggersOfJob(JobKey jobKey) throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; - - return resources.getJobStore().getTriggersForJob(ctxt, jobName, - groupName); + return resources.getJobStore().getTriggersForJob(jobKey); } /** @@ -1128,27 +1482,28 @@ * groups. *

*/ - public String[] getTriggerGroupNames(SchedulingContext ctxt) - throws SchedulerException { + public List getTriggerGroupNames() + throws SchedulerException { validateState(); - return resources.getJobStore().getTriggerGroupNames(ctxt); + return resources.getJobStore().getTriggerGroupNames(); } /** *

* Get the names of all the {@link org.quartz.Trigger}s in - * the given group. + * the matching groups. *

*/ - public String[] getTriggerNames(SchedulingContext ctxt, String groupName) - throws SchedulerException { + public Set getTriggerKeys(GroupMatcher matcher) + throws SchedulerException { validateState(); - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; + if(matcher == null) { + matcher = GroupMatcher.groupEquals(Scheduler.DEFAULT_GROUP); + } - return resources.getJobStore().getTriggerNames(ctxt, groupName); + return resources.getJobStore().getTriggerKeys(matcher); } /** @@ -1157,14 +1512,10 @@ * instance with the given name and group. *

*/ - public JobDetail getJobDetail(SchedulingContext ctxt, String jobName, - String jobGroup) throws SchedulerException { + public JobDetail getJobDetail(JobKey jobKey) throws SchedulerException { validateState(); - if(jobGroup == null) - jobGroup = Scheduler.DEFAULT_GROUP; - - return resources.getJobStore().retrieveJob(ctxt, jobName, jobGroup); + return resources.getJobStore().retrieveJob(jobKey); } /** @@ -1173,36 +1524,67 @@ * group. *

*/ - public Trigger getTrigger(SchedulingContext ctxt, String triggerName, - String triggerGroup) throws SchedulerException { + public Trigger getTrigger(TriggerKey triggerKey) throws SchedulerException { validateState(); - if(triggerGroup == null) - triggerGroup = Scheduler.DEFAULT_GROUP; + return resources.getJobStore().retrieveTrigger(triggerKey); + } + + /** + * Determine whether a {@link Job} with the given identifier already + * exists within the scheduler. + * + * @param jobKey the identifier to check for + * @return true if a Job exists with the given identifier + * @throws SchedulerException + */ + public boolean checkExists(JobKey jobKey) throws SchedulerException { + validateState(); + + return resources.getJobStore().checkExists(jobKey); - return resources.getJobStore().retrieveTrigger(ctxt, triggerName, - triggerGroup); } + + /** + * Determine whether a {@link Trigger} with the given identifier already + * exists within the scheduler. + * + * @param triggerKey the identifier to check for + * @return true if a Trigger exists with the given identifier + * @throws SchedulerException + */ + public boolean checkExists(TriggerKey triggerKey) throws SchedulerException { + validateState(); + return resources.getJobStore().checkExists(triggerKey); + + } + /** + * Clears (deletes!) all scheduling data - all {@link Job}s, {@link Trigger}s + * {@link Calendar}s. + * + * @throws SchedulerException + */ + public void clear() throws SchedulerException { + validateState(); + + resources.getJobStore().clearAllSchedulingData(); + notifySchedulerListenersUnscheduled(null); + } + + + /** *

* Get the current state of the identified {@link Trigger}. *

- * - * @see Trigger#STATE_NORMAL - * @see Trigger#STATE_PAUSED - * @see Trigger#STATE_COMPLETE - * @see Trigger#STATE_ERROR +J * + * @see TriggerState */ - public int getTriggerState(SchedulingContext ctxt, String triggerName, - String triggerGroup) throws SchedulerException { + public TriggerState getTriggerState(TriggerKey triggerKey) throws SchedulerException { validateState(); - if(triggerGroup == null) - triggerGroup = Scheduler.DEFAULT_GROUP; - - return resources.getJobStore().getTriggerState(ctxt, triggerName, - triggerGroup); + return resources.getJobStore().getTriggerState(triggerKey); } /** @@ -1215,11 +1597,10 @@ * the same name already exists, and replace is * false. */ - public void addCalendar(SchedulingContext ctxt, String calName, - Calendar calendar, boolean replace, boolean updateTriggers) throws SchedulerException { + public void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) throws SchedulerException { validateState(); - resources.getJobStore().storeCalendar(ctxt, calName, calendar, replace, updateTriggers); + resources.getJobStore().storeCalendar(calName, calendar, replace, updateTriggers); } /** @@ -1231,324 +1612,278 @@ * @throws SchedulerException * if there is an internal Scheduler error. */ - public boolean deleteCalendar(SchedulingContext ctxt, String calName) - throws SchedulerException { + public boolean deleteCalendar(String calName) + throws SchedulerException { validateState(); - return resources.getJobStore().removeCalendar(ctxt, calName); + return resources.getJobStore().removeCalendar(calName); } /** *

* Get the {@link Calendar} instance with the given name. *

*/ - public Calendar getCalendar(SchedulingContext ctxt, String calName) - throws SchedulerException { + public Calendar getCalendar(String calName) + throws SchedulerException { validateState(); - return resources.getJobStore().retrieveCalendar(ctxt, calName); + return resources.getJobStore().retrieveCalendar(calName); } /** *

* Get the names of all registered {@link Calendar}s. *

*/ - public String[] getCalendarNames(SchedulingContext ctxt) - throws SchedulerException { + public List getCalendarNames() + throws SchedulerException { validateState(); - return resources.getJobStore().getCalendarNames(ctxt); + return resources.getJobStore().getCalendarNames(); } + public ListenerManager getListenerManager() { + return listenerManager; + } + /** *

* Add the given {@link org.quartz.JobListener} to the - * Scheduler'sglobal list. + * Scheduler's internal list. *

- * - *

- * Listeners in the 'global' list receive notification of execution events - * for ALL {@link org.quartz.Job}s. - *

*/ - public void addGlobalJobListener(JobListener jobListener) { + public void addInternalJobListener(JobListener jobListener) { if (jobListener.getName() == null - || jobListener.getName().length() == 0) - throw new IllegalArgumentException( - "JobListener name cannot be empty."); - - globalJobListeners.add(jobListener); + || jobListener.getName().length() == 0) { + throw new IllegalArgumentException( + "JobListener name cannot be empty."); + } + + synchronized (internalJobListeners) { + internalJobListeners.put(jobListener.getName(), jobListener); + } } /** *

- * Add the given {@link org.quartz.JobListener} to the - * Scheduler's list, of registered JobListeners. - */ - public void addJobListener(JobListener jobListener) { - if (jobListener.getName() == null - || jobListener.getName().length() == 0) - throw new IllegalArgumentException( - "JobListener name cannot be empty."); - - jobListeners.put(jobListener.getName(), jobListener); - } - - /** - *

- * Remove the given {@link org.quartz.JobListener} from the - * Scheduler's list of global listeners. + * Remove the identified {@link JobListener} from the Scheduler's + * list of internal listeners. *

* - * @return true if the identifed listener was found in the list, and + * @return true if the identified listener was found in the list, and * removed. */ - public boolean removeGlobalJobListener(JobListener jobListener) { - return globalJobListeners.remove(jobListener); + public boolean removeInternalJobListener(String name) { + synchronized (internalJobListeners) { + return (internalJobListeners.remove(name) != null); + } } - + /** *

- * Remove the identifed {@link org.quartz.JobListener} from - * the Scheduler's list of registered listeners. + * Get a List containing all of the {@link org.quartz.JobListener}s + * in the Scheduler's internal list. *

- * - * @return true if the identifed listener was found in the list, and - * removed. */ - public boolean removeJobListener(String name) { - Object o = jobListeners.remove(name); - - if (o != null) return true; - - return false; + public List getInternalJobListeners() { + synchronized (internalJobListeners) { + return java.util.Collections.unmodifiableList(new LinkedList(internalJobListeners.values())); + } } /** *

- * Get a List containing all of the {@link org.quartz.JobListener} - * s in the Scheduler'sglobal list. - *

- */ - public List getGlobalJobListeners() { - return new LinkedList(globalJobListeners); - } - - /** - *

- * Get a Set containing the names of all the non-global{@link org.quartz.JobListener} - * s registered with the Scheduler. - *

- */ - public Set getJobListenerNames() { - return Collections.unmodifiableSet(jobListeners.keySet()); - } - - /** - *

- * Get the non-global{@link org.quartz.JobListener} + * Get the internal {@link org.quartz.JobListener} * that has the given name. *

*/ - public JobListener getJobListener(String name) { - return (JobListener) jobListeners.get(name); + public JobListener getInternalJobListener(String name) { + synchronized (internalJobListeners) { + return internalJobListeners.get(name); + } } - + /** *

* Add the given {@link org.quartz.TriggerListener} to the - * Scheduler'sglobal list. + * Scheduler's internal list. *

- * - *

- * Listeners in the 'global' list receive notification of execution events - * for ALL {@link org.quartz.Trigger}s. - *

*/ - public void addGlobalTriggerListener(TriggerListener triggerListener) { + public void addInternalTriggerListener(TriggerListener triggerListener) { if (triggerListener.getName() == null - || triggerListener.getName().length() == 0) - throw new IllegalArgumentException( - "TriggerListener name cannot be empty."); + || triggerListener.getName().length() == 0) { + throw new IllegalArgumentException( + "TriggerListener name cannot be empty."); + } - globalTriggerListeners.add(triggerListener); + synchronized (internalTriggerListeners) { + internalTriggerListeners.put(triggerListener.getName(), triggerListener); + } } /** *

- * Add the given {@link org.quartz.TriggerListener} to the - * Scheduler's list, of registered TriggerListeners. - */ - public void addTriggerListener(TriggerListener triggerListener) { - if (triggerListener.getName() == null - || triggerListener.getName().length() == 0) - throw new IllegalArgumentException( - "TriggerListener name cannot be empty."); - - triggerListeners.put(triggerListener.getName(), triggerListener); - } - - /** - *

- * Remove the given {@link org.quartz.TriggerListener} from - * the Scheduler's list of global listeners. + * Remove the identified {@link TriggerListener} from the Scheduler's + * list of internal listeners. *

* - * @return true if the identifed listener was found in the list, and + * @return true if the identified listener was found in the list, and * removed. */ - public boolean removeGlobalTriggerListener(TriggerListener triggerListener) { - return globalTriggerListeners.remove(triggerListener); + public boolean removeinternalTriggerListener(String name) { + synchronized (internalTriggerListeners) { + return (internalTriggerListeners.remove(name) != null); + } } /** *

- * Remove the identifed {@link org.quartz.TriggerListener} - * from the Scheduler's list of registered listeners. + * Get a list containing all of the {@link org.quartz.TriggerListener}s + * in the Scheduler's internal list. *

- * - * @return true if the identifed listener was found in the list, and - * removed. */ - public boolean removeTriggerListener(String name) { - Object o = triggerListeners.remove(name); - - if (o != null) return true; - - return false; + public List getInternalTriggerListeners() { + synchronized (internalTriggerListeners) { + return java.util.Collections.unmodifiableList(new LinkedList(internalTriggerListeners.values())); + } } /** *

- * Get a list containing all of the {@link org.quartz.TriggerListener} - * s in the Scheduler'sglobal list. + * Get the internal {@link TriggerListener} that + * has the given name. *

*/ - public List getGlobalTriggerListeners() { - return new LinkedList(globalTriggerListeners); + public TriggerListener getInternalTriggerListener(String name) { + synchronized (internalTriggerListeners) { + return internalTriggerListeners.get(name); + } } /** *

- * Get a Set containing the names of all the non-global{@link org.quartz.TriggerListener} - * s registered with the Scheduler. - *

- */ - public Set getTriggerListenerNames() { - return Collections.unmodifiableSet(triggerListeners.keySet()); - } - - /** - *

- * Get the non-global{@link org.quartz.TriggerListener} - * that has the given name. - *

- */ - public TriggerListener getTriggerListener(String name) { - return (TriggerListener) triggerListeners.get(name); - } - - /** - *

* Register the given {@link SchedulerListener} with the - * Scheduler. + * Scheduler's list of internal listeners. *

*/ - public void addSchedulerListener(SchedulerListener schedulerListener) { - schedulerListeners.add(schedulerListener); + public void addInternalSchedulerListener(SchedulerListener schedulerListener) { + synchronized (internalSchedulerListeners) { + internalSchedulerListeners.add(schedulerListener); + } } /** *

* Remove the given {@link SchedulerListener} from the - * Scheduler. + * Scheduler's list of internal listeners. *

* - * @return true if the identifed listener was found in the list, and + * @return true if the identified listener was found in the list, and * removed. */ - public boolean removeSchedulerListener(SchedulerListener schedulerListener) { - return schedulerListeners.remove(schedulerListener); + public boolean removeInternalSchedulerListener(SchedulerListener schedulerListener) { + synchronized (internalSchedulerListeners) { + return internalSchedulerListeners.remove(schedulerListener); + } } /** *

- * Get a List containing all of the {@link SchedulerListener} - * s registered with the Scheduler. + * Get a List containing all of the internal {@link SchedulerListener}s + * registered with the Scheduler. *

*/ - public List getSchedulerListeners() { - return (List) schedulerListeners.clone(); + public List getInternalSchedulerListeners() { + synchronized (internalSchedulerListeners) { + return java.util.Collections.unmodifiableList(new ArrayList(internalSchedulerListeners)); + } } - protected void notifyJobStoreJobComplete(SchedulingContext ctxt, - Trigger trigger, JobDetail detail, int instCode) - throws JobPersistenceException { - - resources.getJobStore().triggeredJobComplete(ctxt, trigger, detail, - instCode); + protected void notifyJobStoreJobComplete(OperableTrigger trigger, JobDetail detail, CompletedExecutionInstruction instCode) { + resources.getJobStore().triggeredJobComplete(trigger, detail, instCode); } - protected void notifySchedulerThread() { - if (isSignalOnSchedulingChange()) schedThread.signalSchedulingChange(); + protected void notifyJobStoreJobVetoed(OperableTrigger trigger, JobDetail detail, CompletedExecutionInstruction instCode) { + resources.getJobStore().triggeredJobComplete(trigger, detail, instCode); } - private List buildTriggerListenerList(String[] additionalLstnrs) - throws SchedulerException { - List triggerListeners = getGlobalTriggerListeners(); - for (int i = 0; i < additionalLstnrs.length; i++) { - TriggerListener tl = getTriggerListener(additionalLstnrs[i]); - - if (tl != null) triggerListeners.add(tl); - else - throw new SchedulerException("TriggerListener '" - + additionalLstnrs[i] + "' not found.", - SchedulerException.ERR_TRIGGER_LISTENER_NOT_FOUND); + protected void notifySchedulerThread(long candidateNewNextFireTime) { + if (isSignalOnSchedulingChange()) { + signaler.signalSchedulingChange(candidateNewNextFireTime); } + } - return triggerListeners; + private List buildTriggerListenerList() + throws SchedulerException { + List allListeners = new LinkedList(); + allListeners.addAll(getListenerManager().getTriggerListeners()); + allListeners.addAll(getInternalTriggerListeners()); + + return allListeners; } - private List buildJobListenerList(String[] additionalLstnrs) - throws SchedulerException { - List jobListeners = getGlobalJobListeners(); - for (int i = 0; i < additionalLstnrs.length; i++) { - JobListener jl = getJobListener(additionalLstnrs[i]); + private List buildJobListenerList() + throws SchedulerException { + List allListeners = new LinkedList(); + allListeners.addAll(getListenerManager().getJobListeners()); + allListeners.addAll(getInternalJobListeners()); - if (jl != null) jobListeners.add(jl); - else - throw new SchedulerException("JobListener '" - + additionalLstnrs[i] + "' not found.", - SchedulerException.ERR_JOB_LISTENER_NOT_FOUND); + return allListeners; + } + + private List buildSchedulerListenerList() { + List allListeners = new LinkedList(); + allListeners.addAll(getListenerManager().getSchedulerListeners()); + allListeners.addAll(getInternalSchedulerListeners()); + + return allListeners; + } + + private boolean matchJobListener(JobListener listener, JobKey key) { + List> matchers = getListenerManager().getJobListenerMatchers(listener.getName()); + if(matchers == null) + return true; + for(Matcher matcher: matchers) { + if(matcher.isMatch(key)) + return true; } + return false; + } - return jobListeners; + private boolean matchTriggerListener(TriggerListener listener, TriggerKey key) { + List> matchers = getListenerManager().getTriggerListenerMatchers(listener.getName()); + if(matchers == null) + return true; + for(Matcher matcher: matchers) { + if(matcher.isMatch(key)) + return true; + } + return false; } public boolean notifyTriggerListenersFired(JobExecutionContext jec) - throws SchedulerException { - // build a list of all trigger listeners that are to be notified... - List triggerListeners = buildTriggerListenerList(jec.getTrigger() - .getTriggerListenerNames()); + throws SchedulerException { boolean vetoedExecution = false; + // build a list of all trigger listeners that are to be notified... + List triggerListeners = buildTriggerListenerList(); + // notify all trigger listeners in the list - java.util.Iterator itr = triggerListeners.iterator(); - while (itr.hasNext()) { - TriggerListener tl = (TriggerListener) itr.next(); + for(TriggerListener tl: triggerListeners) { try { + if(!matchTriggerListener(tl, jec.getTrigger().getKey())) + continue; tl.triggerFired(jec.getTrigger(), jec); - if(tl.vetoJobExecution(jec.getTrigger(), jec)) + if(tl.vetoJobExecution(jec.getTrigger(), jec)) { vetoedExecution = true; + } } catch (Exception e) { SchedulerException se = new SchedulerException( "TriggerListener '" + tl.getName() + "' threw exception: " + e.getMessage(), e); - se.setErrorCode(SchedulerException.ERR_TRIGGER_LISTENER); throw se; } } @@ -1558,88 +1893,80 @@ public void notifyTriggerListenersMisfired(Trigger trigger) - throws SchedulerException { + throws SchedulerException { // build a list of all trigger listeners that are to be notified... - List triggerListeners = buildTriggerListenerList(trigger - .getTriggerListenerNames()); + List triggerListeners = buildTriggerListenerList(); // notify all trigger listeners in the list - java.util.Iterator itr = triggerListeners.iterator(); - while (itr.hasNext()) { - TriggerListener tl = (TriggerListener) itr.next(); + for(TriggerListener tl: triggerListeners) { try { + if(!matchTriggerListener(tl, trigger.getKey())) + continue; tl.triggerMisfired(trigger); } catch (Exception e) { SchedulerException se = new SchedulerException( "TriggerListener '" + tl.getName() + "' threw exception: " + e.getMessage(), e); - se.setErrorCode(SchedulerException.ERR_TRIGGER_LISTENER); throw se; } } } public void notifyTriggerListenersComplete(JobExecutionContext jec, - int instCode) throws SchedulerException { + CompletedExecutionInstruction instCode) throws SchedulerException { // build a list of all trigger listeners that are to be notified... - List triggerListeners = buildTriggerListenerList(jec.getTrigger() - .getTriggerListenerNames()); + List triggerListeners = buildTriggerListenerList(); // notify all trigger listeners in the list - java.util.Iterator itr = triggerListeners.iterator(); - while (itr.hasNext()) { - TriggerListener tl = (TriggerListener) itr.next(); + for(TriggerListener tl: triggerListeners) { try { + if(!matchTriggerListener(tl, jec.getTrigger().getKey())) + continue; tl.triggerComplete(jec.getTrigger(), jec, instCode); } catch (Exception e) { SchedulerException se = new SchedulerException( "TriggerListener '" + tl.getName() + "' threw exception: " + e.getMessage(), e); - se.setErrorCode(SchedulerException.ERR_TRIGGER_LISTENER); throw se; } } } public void notifyJobListenersToBeExecuted(JobExecutionContext jec) - throws SchedulerException { + throws SchedulerException { // build a list of all job listeners that are to be notified... - List jobListeners = buildJobListenerList(jec.getJobDetail() - .getJobListenerNames()); + List jobListeners = buildJobListenerList(); // notify all job listeners - java.util.Iterator itr = jobListeners.iterator(); - while (itr.hasNext()) { - JobListener jl = (JobListener) itr.next(); + for(JobListener jl: jobListeners) { try { + if(!matchJobListener(jl, jec.getJobDetail().getKey())) + continue; jl.jobToBeExecuted(jec); } catch (Exception e) { SchedulerException se = new SchedulerException( "JobListener '" + jl.getName() + "' threw exception: " + e.getMessage(), e); - se.setErrorCode(SchedulerException.ERR_JOB_LISTENER); throw se; } } } public void notifyJobListenersWasVetoed(JobExecutionContext jec) - throws SchedulerException { + throws SchedulerException { // build a list of all job listeners that are to be notified... - List jobListeners = buildJobListenerList(jec.getJobDetail() - .getJobListenerNames()); + List jobListeners = buildJobListenerList(); // notify all job listeners - java.util.Iterator itr = jobListeners.iterator(); - while (itr.hasNext()) { - JobListener jl = (JobListener) itr.next(); + for(JobListener jl: jobListeners) { try { + if(!matchJobListener(jl, jec.getJobDetail().getKey())) + continue; jl.jobExecutionVetoed(jec); } catch (Exception e) { SchedulerException se = new SchedulerException( "JobListener '" + jl.getName() + "' threw exception: " + e.getMessage(), e); - se.setErrorCode(SchedulerException.ERR_JOB_LISTENER); throw se; } } @@ -1648,33 +1975,29 @@ public void notifyJobListenersWasExecuted(JobExecutionContext jec, JobExecutionException je) throws SchedulerException { // build a list of all job listeners that are to be notified... - List jobListeners = buildJobListenerList(jec.getJobDetail() - .getJobListenerNames()); + List jobListeners = buildJobListenerList(); // notify all job listeners - java.util.Iterator itr = jobListeners.iterator(); - while (itr.hasNext()) { - JobListener jl = (JobListener) itr.next(); + for(JobListener jl: jobListeners) { try { + if(!matchJobListener(jl, jec.getJobDetail().getKey())) + continue; jl.jobWasExecuted(jec, je); } catch (Exception e) { SchedulerException se = new SchedulerException( "JobListener '" + jl.getName() + "' threw exception: " + e.getMessage(), e); - se.setErrorCode(SchedulerException.ERR_JOB_LISTENER); throw se; } } } public void notifySchedulerListenersError(String msg, SchedulerException se) { // build a list of all scheduler listeners that are to be notified... - List schedListeners = getSchedulerListeners(); + List schedListeners = buildSchedulerListenerList(); // notify all scheduler listeners - java.util.Iterator itr = schedListeners.iterator(); - while (itr.hasNext()) { - SchedulerListener sl = (SchedulerListener) itr.next(); + for(SchedulerListener sl: schedListeners) { try { sl.schedulerError(msg, se); } catch (Exception e) { @@ -1690,140 +2013,237 @@ public void notifySchedulerListenersSchduled(Trigger trigger) { // build a list of all scheduler listeners that are to be notified... - List schedListeners = getSchedulerListeners(); + List schedListeners = buildSchedulerListenerList(); // notify all scheduler listeners - java.util.Iterator itr = schedListeners.iterator(); - while (itr.hasNext()) { - SchedulerListener sl = (SchedulerListener) itr.next(); + for(SchedulerListener sl: schedListeners) { try { sl.jobScheduled(trigger); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of scheduled job." - + " Triger=" + trigger.getFullName(), e); + + " Triger=" + trigger.getKey(), e); } } } - public void notifySchedulerListenersUnschduled(String triggerName, - String triggerGroup) { + public void notifySchedulerListenersUnscheduled(TriggerKey triggerKey) { // build a list of all scheduler listeners that are to be notified... - List schedListeners = getSchedulerListeners(); + List schedListeners = buildSchedulerListenerList(); // notify all scheduler listeners - java.util.Iterator itr = schedListeners.iterator(); - while (itr.hasNext()) { - SchedulerListener sl = (SchedulerListener) itr.next(); + for(SchedulerListener sl: schedListeners) { try { - sl.jobUnscheduled(triggerName, triggerGroup); + if(triggerKey == null) + sl.schedulingDataCleared(); + else + sl.jobUnscheduled(triggerKey); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of unscheduled job." - + " Triger=" + triggerGroup + "." - + triggerName, e); + + " Triger=" + (triggerKey == null ? "ALL DATA" : triggerKey), e); } } } public void notifySchedulerListenersFinalized(Trigger trigger) { // build a list of all scheduler listeners that are to be notified... - List schedListeners = getSchedulerListeners(); + List schedListeners = buildSchedulerListenerList(); // notify all scheduler listeners - java.util.Iterator itr = schedListeners.iterator(); - while (itr.hasNext()) { - SchedulerListener sl = (SchedulerListener) itr.next(); + for(SchedulerListener sl: schedListeners) { try { sl.triggerFinalized(trigger); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of finalized trigger." - + " Triger=" + trigger.getFullName(), e); + + " Triger=" + trigger.getKey(), e); } } } - public void notifySchedulerListenersPausedTrigger(String name, String group) { - // build a list of all job listeners that are to be notified... - List schedListeners = getSchedulerListeners(); + public void notifySchedulerListenersPausedTrigger(TriggerKey triggerKey) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); // notify all scheduler listeners - java.util.Iterator itr = schedListeners.iterator(); - while (itr.hasNext()) { - SchedulerListener sl = (SchedulerListener) itr.next(); + for(SchedulerListener sl: schedListeners) { try { - sl.triggersPaused(name, group); + sl.triggerPaused(triggerKey); } catch (Exception e) { getLog().error( - "Error while notifying SchedulerListener of paused trigger/group." - + " Triger=" + group + "." + name, e); + "Error while notifying SchedulerListener of paused trigger: " + + triggerKey, e); } } } - public void notifySchedulerListenersResumedTrigger(String name, String group) { - // build a list of all job listeners that are to be notified... - List schedListeners = getSchedulerListeners(); + public void notifySchedulerListenersPausedTriggers(String group) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); // notify all scheduler listeners - java.util.Iterator itr = schedListeners.iterator(); - while (itr.hasNext()) { - SchedulerListener sl = (SchedulerListener) itr.next(); + for(SchedulerListener sl: schedListeners) { try { - sl.triggersResumed(name, group); + sl.triggersPaused(group); } catch (Exception e) { getLog().error( - "Error while notifying SchedulerListener of resumed trigger/group." - + " Triger=" + group + "." + name, e); + "Error while notifying SchedulerListener of paused trigger group." + + group, e); } } } + + public void notifySchedulerListenersResumedTrigger(TriggerKey key) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); - public void notifySchedulerListenersPausedJob(String name, String group) { - // build a list of all job listeners that are to be notified... - List schedListeners = getSchedulerListeners(); + // notify all scheduler listeners + for(SchedulerListener sl: schedListeners) { + try { + sl.triggerResumed(key); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of resumed trigger: " + + key, e); + } + } + } + public void notifySchedulerListenersResumedTriggers(String group) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + // notify all scheduler listeners - java.util.Iterator itr = schedListeners.iterator(); - while (itr.hasNext()) { - SchedulerListener sl = (SchedulerListener) itr.next(); + for(SchedulerListener sl: schedListeners) { try { - sl.jobsPaused(name, group); + sl.triggersResumed(group); } catch (Exception e) { getLog().error( - "Error while notifying SchedulerListener of paused job/group." - + " Job=" + group + "." + name, e); + "Error while notifying SchedulerListener of resumed group: " + + group, e); } } } - public void notifySchedulerListenersResumedJob(String name, String group) { - // build a list of all job listeners that are to be notified... - List schedListeners = getSchedulerListeners(); + public void notifySchedulerListenersPausedJob(JobKey key) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); // notify all scheduler listeners - java.util.Iterator itr = schedListeners.iterator(); - while (itr.hasNext()) { - SchedulerListener sl = (SchedulerListener) itr.next(); + for(SchedulerListener sl: schedListeners) { try { - sl.jobsResumed(name, group); + sl.jobPaused(key); } catch (Exception e) { getLog().error( - "Error while notifying SchedulerListener of resumed job/group." - + " Job=" + group + "." + name, e); + "Error while notifying SchedulerListener of paused job: " + + key, e); } } } + public void notifySchedulerListenersPausedJobs(String group) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + + // notify all scheduler listeners + for(SchedulerListener sl: schedListeners) { + try { + sl.jobsPaused(group); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of paused job group: " + + group, e); + } + } + } + + public void notifySchedulerListenersResumedJob(JobKey key) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + + // notify all scheduler listeners + for(SchedulerListener sl: schedListeners) { + try { + sl.jobResumed(key); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of resumed job: " + + key, e); + } + } + } + + public void notifySchedulerListenersResumedJobs(String group) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + + // notify all scheduler listeners + for(SchedulerListener sl: schedListeners) { + try { + sl.jobsResumed(group); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of resumed job group: " + + group, e); + } + } + } + + public void notifySchedulerListenersInStandbyMode() { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + + // notify all scheduler listeners + for(SchedulerListener sl: schedListeners) { + try { + sl.schedulerInStandbyMode(); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of inStandByMode.", + e); + } + } + } + + public void notifySchedulerListenersStarted() { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + + // notify all scheduler listeners + for(SchedulerListener sl: schedListeners) { + try { + sl.schedulerStarted(); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of startup.", + e); + } + } + } + + public void notifySchedulerListenersStarting() { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + + // notify all scheduler listeners + for (SchedulerListener sl : schedListeners) { + try { + sl.schedulerStarting(); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of startup.", + e); + } + } + } + public void notifySchedulerListenersShutdown() { - // build a list of all job listeners that are to be notified... - List schedListeners = getSchedulerListeners(); + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); // notify all scheduler listeners - java.util.Iterator itr = schedListeners.iterator(); - while (itr.hasNext()) { - SchedulerListener sl = (SchedulerListener) itr.next(); + for(SchedulerListener sl: schedListeners) { try { sl.schedulerShutdown(); } catch (Exception e) { @@ -1833,22 +2253,60 @@ } } } - /** - *

- * Add the given {@link org.quartz.spi.SchedulerPlugin} to - * the Scheduler. This method expects the plugin's - * "initialize" method to be invoked externally (either before or after - * this method is called). - */ - public void addSchedulerPlugin(SchedulerPlugin plugin) { - schedulerPlugins.add(plugin); + + public void notifySchedulerListenersShuttingdown() { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + + // notify all scheduler listeners + for(SchedulerListener sl: schedListeners) { + try { + sl.schedulerShuttingdown(); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of shutdown.", + e); + } + } } + + public void notifySchedulerListenersJobAdded(JobDetail jobDetail) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + // notify all scheduler listeners + for(SchedulerListener sl: schedListeners) { + try { + sl.jobAdded(jobDetail); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of JobAdded.", + e); + } + } + } + public void notifySchedulerListenersJobDeleted(JobKey jobKey) { + // build a list of all scheduler listeners that are to be notified... + List schedListeners = buildSchedulerListenerList(); + + // notify all scheduler listeners + for(SchedulerListener sl: schedListeners) { + try { + sl.jobDeleted(jobKey); + } catch (Exception e) { + getLog().error( + "Error while notifying SchedulerListener of JobAdded.", + e); + } + } + } + public void setJobFactory(JobFactory factory) throws SchedulerException { - if(factory == null) + if(factory == null) { throw new IllegalArgumentException("JobFactory cannot be set to null!"); + } getLog().info("JobFactory set to: " + factory); @@ -1861,61 +2319,91 @@ /** - * Interrupt all instances of the identified InterruptableJob. + * Interrupt all instances of the identified InterruptableJob executing in + * this Scheduler instance. * - * @see org.quartz.core.RemotableQuartzScheduler#interrupt(java.lang.String, java.lang.String) + *

+ * 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 org.quartz.core.RemotableQuartzScheduler#interrupt(JobKey) */ - public boolean interrupt(SchedulingContext ctxt, String jobName, String groupName) throws UnableToInterruptJobException { + public boolean interrupt(JobKey jobKey) throws UnableToInterruptJobException { - if(groupName == null) - groupName = Scheduler.DEFAULT_GROUP; + List jobs = getCurrentlyExecutingJobs(); - 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(); + for(JobExecutionContext jec : jobs) { jobDetail = jec.getJobDetail(); - if (jobName.equals(jobDetail.getName()) - && groupName.equals(jobDetail.getGroup())){ + if (jobKey.equals(jobDetail.getKey())) { 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()); - + "Job " + jobDetail.getKey() + + " can not be interrupted, since it does not implement " + + InterruptableJob.class.getName()); } } } return interrupted; } + + /** + * Interrupt the identified InterruptableJob executing in this Scheduler instance. + * + *

+ * 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 org.quartz.core.RemotableQuartzScheduler#interrupt(JobKey) + */ + public boolean interrupt(String fireInstanceId) throws UnableToInterruptJobException { + List jobs = getCurrentlyExecutingJobs(); + + Job job = null; + + for(JobExecutionContext jec : jobs) { + if (jec.getFireInstanceId().equals(fireInstanceId)) { + job = jec.getJobInstance(); + if (job instanceof InterruptableJob) { + ((InterruptableJob)job).interrupt(); + return true; + } else { + throw new UnableToInterruptJobException( + "Job " + jec.getJobDetail().getKey() + + " can not be interrupted, since it does not implement " + + InterruptableJob.class.getName()); + } + } + } + + return false; + } private void shutdownPlugins() { - java.util.Iterator itr = schedulerPlugins.iterator(); + java.util.Iterator itr = resources.getSchedulerPlugins().iterator(); while (itr.hasNext()) { - SchedulerPlugin plugin = (SchedulerPlugin) itr.next(); + SchedulerPlugin plugin = itr.next(); plugin.shutdown(); } } private void startPlugins() { - java.util.Iterator itr = schedulerPlugins.iterator(); + java.util.Iterator itr = resources.getSchedulerPlugins().iterator(); while (itr.hasNext()) { - SchedulerPlugin plugin = (SchedulerPlugin) itr.next(); + SchedulerPlugin plugin = itr.next(); plugin.start(); } } @@ -1928,96 +2416,15 @@ // ///////////////////////////////////////////////////////////////////////////// -class ErrorLogger implements SchedulerListener { - - public static Log getLog() { - return LogFactory.getLog(ErrorLogger.class); - } - +class ErrorLogger extends SchedulerListenerSupport { ErrorLogger() { } - - public void jobScheduled(Trigger trigger) { - // do nothing... - } - - public void jobUnscheduled(String triggerName, String triggerGroup) { - // do nothing... - } - - public void triggerFinalized(Trigger trigger) { - // do nothing... - } - - /** - *

- * 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. - *

- */ - public void triggersPaused(String triggerName, String triggerGroup) { - // do nothing... - } - - /** - *

- * 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. - *

- */ - public void triggersResumed(String triggerName, String triggerGroup) { - // do nothing... - } - - /** - *

- * Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} - * or group of {@link org.quartz.JobDetail}s has been - * paused. - *

- * - *

- * If a group was paused, then the jobName parameter will be - * null. - *

- */ - public void jobsPaused(String jobName, String jobGroup) { - // do nothing... - } - - /** - *

- * Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} - * or group of {@link org.quartz.JobDetail}s has been - * un-paused. - *

- * - *

- * If a group was paused, then the jobName parameter will be - * null. - *

- */ - public void jobsResumed(String jobName, String jobGroup) { - // do nothing... - } - + + @Override public void schedulerError(String msg, SchedulerException cause) { getLog().error(msg, cause); } - public void schedulerShutdown() { - // do nothing... - } } ///////////////////////////////////////////////////////////////////////////// @@ -2027,9 +2434,9 @@ ///////////////////////////////////////////////////////////////////////////// class ExecutingJobsManager implements JobListener { - HashMap executingJobs = new HashMap(); + HashMap executingJobs = new HashMap(); - int numJobsFired = 0; + AtomicInteger numJobsFired = new AtomicInteger(0); ExecutingJobsManager() { } @@ -2045,28 +2452,28 @@ } public void jobToBeExecuted(JobExecutionContext context) { - numJobsFired++; + numJobsFired.incrementAndGet(); synchronized (executingJobs) { executingJobs - .put(context.getTrigger().getFireInstanceId(), context); + .put(((OperableTrigger)context.getTrigger()).getFireInstanceId(), context); } } public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { synchronized (executingJobs) { - executingJobs.remove(context.getTrigger().getFireInstanceId()); + executingJobs.remove(((OperableTrigger)context.getTrigger()).getFireInstanceId()); } } public int getNumJobsFired() { - return numJobsFired; + return numJobsFired.get(); } - public List getExecutingJobs() { + public List getExecutingJobs() { synchronized (executingJobs) { - return java.util.Collections.unmodifiableList(new ArrayList( + return java.util.Collections.unmodifiableList(new ArrayList( executingJobs.values())); } } Index: 3rdParty_sources/quartz/org/quartz/core/QuartzSchedulerMBeanImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/QuartzSchedulerMBeanImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/QuartzSchedulerMBeanImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,1011 @@ +package org.quartz.core; + +import static org.quartz.JobKey.jobKey; +import static org.quartz.TriggerKey.triggerKey; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.MethodDescriptor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; + +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.NotCompliantMBeanException; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.StandardMBean; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobKey; +import org.quartz.JobListener; +import org.quartz.SchedulerException; +import org.quartz.SchedulerListener; +import org.quartz.Trigger; +import org.quartz.Trigger.TriggerState; +import org.quartz.TriggerKey; +import org.quartz.core.jmx.JobDetailSupport; +import org.quartz.core.jmx.JobExecutionContextSupport; +import org.quartz.core.jmx.QuartzSchedulerMBean; +import org.quartz.core.jmx.TriggerSupport; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.impl.triggers.AbstractTrigger; +import org.quartz.spi.OperableTrigger; + +public class QuartzSchedulerMBeanImpl extends StandardMBean implements + NotificationEmitter, QuartzSchedulerMBean, JobListener, + SchedulerListener { + private static final MBeanNotificationInfo[] NOTIFICATION_INFO; + + private final QuartzScheduler scheduler; + private boolean sampledStatisticsEnabled; + private SampledStatistics sampledStatistics; + + private final static SampledStatistics NULL_SAMPLED_STATISTICS = new NullSampledStatisticsImpl(); + + static { + final String[] notifTypes = new String[] { SCHEDULER_STARTED, + SCHEDULER_PAUSED, SCHEDULER_SHUTDOWN, }; + final String name = Notification.class.getName(); + final String description = "QuartzScheduler JMX Event"; + NOTIFICATION_INFO = new MBeanNotificationInfo[] { new MBeanNotificationInfo( + notifTypes, name, description), }; + } + + /** + * emitter + */ + protected final Emitter emitter = new Emitter(); + + /** + * sequenceNumber + */ + protected final AtomicLong sequenceNumber = new AtomicLong(); + + /** + * QuartzSchedulerMBeanImpl + * + * @throws NotCompliantMBeanException + */ + protected QuartzSchedulerMBeanImpl(QuartzScheduler scheduler) + throws NotCompliantMBeanException { + super(QuartzSchedulerMBean.class); + this.scheduler = scheduler; + this.scheduler.addInternalJobListener(this); + this.scheduler.addInternalSchedulerListener(this); + this.sampledStatistics = NULL_SAMPLED_STATISTICS; + this.sampledStatisticsEnabled = false; + } + + public TabularData getCurrentlyExecutingJobs() throws Exception { + try { + List currentlyExecutingJobs = scheduler.getCurrentlyExecutingJobs(); + return JobExecutionContextSupport.toTabularData(currentlyExecutingJobs); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public TabularData getAllJobDetails() throws Exception { + try { + List detailList = new ArrayList(); + for (String jobGroupName : scheduler.getJobGroupNames()) { + for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(jobGroupName))) { + detailList.add(scheduler.getJobDetail(jobKey)); + } + } + return JobDetailSupport.toTabularData(detailList.toArray(new JobDetail[detailList.size()])); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public List getAllTriggers() throws Exception { + try { + List triggerList = new ArrayList(); + for (String triggerGroupName : scheduler.getTriggerGroupNames()) { + for (TriggerKey triggerKey : scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(triggerGroupName))) { + triggerList.add(scheduler.getTrigger(triggerKey)); + } + } + return TriggerSupport.toCompositeList(triggerList); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void addJob(CompositeData jobDetail, boolean replace) throws Exception { + try { + scheduler.addJob(JobDetailSupport.newJobDetail(jobDetail), replace); + } catch (Exception e) { + throw newPlainException(e); + } + } + + private static void invokeSetter(Object target, String attribute, Object value) throws Exception { + String setterName = "set" + Character.toUpperCase(attribute.charAt(0)) + attribute.substring(1); + Class[] argTypes = {value.getClass()}; + Method setter = findMethod(target.getClass(), setterName, argTypes); + if(setter != null) { + setter.invoke(target, value); + } else { + throw new Exception("Unable to find setter for attribute '" + attribute + + "' and value '" + value + "'"); + } + } + + private static Class getWrapperIfPrimitive(Class c) { + Class result = c; + try { + Field f = c.getField("TYPE"); + f.setAccessible(true); + result = (Class) f.get(null); + } catch (Exception e) { + /**/ + } + return result; + } + + private static Method findMethod(Class targetType, String methodName, + Class[] argTypes) throws IntrospectionException { + BeanInfo beanInfo = Introspector.getBeanInfo(targetType); + if (beanInfo != null) { + for(MethodDescriptor methodDesc: beanInfo.getMethodDescriptors()) { + Method method = methodDesc.getMethod(); + Class[] parameterTypes = method.getParameterTypes(); + if (methodName.equals(method.getName()) && argTypes.length == parameterTypes.length) { + boolean matchedArgTypes = true; + for (int i = 0; i < argTypes.length; i++) { + if (getWrapperIfPrimitive(argTypes[i]) != parameterTypes[i]) { + matchedArgTypes = false; + break; + } + } + if (matchedArgTypes) { + return method; + } + } + } + } + return null; + } + + public void scheduleBasicJob(Map jobDetailInfo, + Map triggerInfo) throws Exception { + try { + JobDetail jobDetail = JobDetailSupport.newJobDetail(jobDetailInfo); + OperableTrigger trigger = TriggerSupport.newTrigger(triggerInfo); + scheduler.deleteJob(jobDetail.getKey()); + scheduler.scheduleJob(jobDetail, trigger); + } catch (ParseException pe) { + throw pe; + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void scheduleJob(Map abstractJobInfo, + Map abstractTriggerInfo) throws Exception { + try { + String triggerClassName = (String) abstractTriggerInfo.remove("triggerClass"); + if(triggerClassName == null) { + throw new IllegalArgumentException("No triggerClass specified"); + } + Class triggerClass = Class.forName(triggerClassName); + Trigger trigger = (Trigger) triggerClass.newInstance(); + + String jobDetailClassName = (String) abstractJobInfo.remove("jobDetailClass"); + if(jobDetailClassName == null) { + throw new IllegalArgumentException("No jobDetailClass specified"); + } + Class jobDetailClass = Class.forName(jobDetailClassName); + JobDetail jobDetail = (JobDetail) jobDetailClass.newInstance(); + + String jobClassName = (String) abstractJobInfo.remove("jobClass"); + if(jobClassName == null) { + throw new IllegalArgumentException("No jobClass specified"); + } + Class jobClass = Class.forName(jobClassName); + abstractJobInfo.put("jobClass", jobClass); + + for(Map.Entry entry : abstractTriggerInfo.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if("jobDataMap".equals(key)) { + value = new JobDataMap((Map)value); + } + invokeSetter(trigger, key, value); + } + + for(Map.Entry entry : abstractJobInfo.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if("jobDataMap".equals(key)) { + value = new JobDataMap((Map)value); + } + invokeSetter(jobDetail, key, value); + } + + AbstractTrigger at = (AbstractTrigger)trigger; + at.setKey(new TriggerKey(at.getName(), at.getGroup())); + + Date startDate = at.getStartTime(); + if(startDate == null || startDate.before(new Date())) { + at.setStartTime(new Date()); + } + + scheduler.deleteJob(jobDetail.getKey()); + scheduler.scheduleJob(jobDetail, trigger); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void scheduleJob(String jobName, String jobGroup, + Map abstractTriggerInfo) throws Exception { + try { + JobKey jobKey = new JobKey(jobName, jobGroup); + JobDetail jobDetail = scheduler.getJobDetail(jobKey); + if(jobDetail == null) { + throw new IllegalArgumentException("No such job '" + jobKey + "'"); + } + + String triggerClassName = (String) abstractTriggerInfo.remove("triggerClass"); + if(triggerClassName == null) { + throw new IllegalArgumentException("No triggerClass specified"); + } + Class triggerClass = Class.forName(triggerClassName); + Trigger trigger = (Trigger) triggerClass.newInstance(); + + for(Map.Entry entry : abstractTriggerInfo.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if("jobDataMap".equals(key)) { + value = new JobDataMap((Map)value); + } + invokeSetter(trigger, key, value); + } + + AbstractTrigger at = (AbstractTrigger)trigger; + at.setKey(new TriggerKey(at.getName(), at.getGroup())); + + Date startDate = at.getStartTime(); + if(startDate == null || startDate.before(new Date())) { + at.setStartTime(new Date()); + } + + scheduler.scheduleJob(trigger); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void addJob(Map abstractJobInfo, boolean replace) throws Exception { + try { + String jobDetailClassName = (String) abstractJobInfo.remove("jobDetailClass"); + if(jobDetailClassName == null) { + throw new IllegalArgumentException("No jobDetailClass specified"); + } + Class jobDetailClass = Class.forName(jobDetailClassName); + JobDetail jobDetail = (JobDetail) jobDetailClass.newInstance(); + + String jobClassName = (String) abstractJobInfo.remove("jobClass"); + if(jobClassName == null) { + throw new IllegalArgumentException("No jobClass specified"); + } + Class jobClass = Class.forName(jobClassName); + abstractJobInfo.put("jobClass", jobClass); + + for(Map.Entry entry : abstractJobInfo.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if("jobDataMap".equals(key)) { + value = new JobDataMap((Map)value); + } + invokeSetter(jobDetail, key, value); + } + + scheduler.addJob(jobDetail, replace); + } catch (Exception e) { + throw newPlainException(e); + } + } + + private Exception newPlainException(Exception e) { + String type = e.getClass().getName(); + if(type.startsWith("java.") || type.startsWith("javax.")) { + return e; + } else { + Exception result = new Exception(e.getMessage()); + result.setStackTrace(e.getStackTrace()); + return result; + } + } + + public void deleteCalendar(String calendarName) throws Exception { + try { + scheduler.deleteCalendar(calendarName); + } catch(Exception e) { + throw newPlainException(e); + } + } + + public boolean deleteJob(String jobName, String jobGroupName) throws Exception { + try { + return scheduler.deleteJob(jobKey(jobName, jobGroupName)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public List getCalendarNames() throws Exception { + try { + return scheduler.getCalendarNames(); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public CompositeData getJobDetail(String jobName, String jobGroupName) + throws Exception { + try { + JobDetail jobDetail = scheduler.getJobDetail(jobKey(jobName, jobGroupName)); + return JobDetailSupport.toCompositeData(jobDetail); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public List getJobGroupNames() throws Exception { + try { + return scheduler.getJobGroupNames(); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public List getJobNames(String groupName) throws Exception { + try { + List jobNames = new ArrayList(); + for(JobKey key: scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) { + jobNames.add(key.getName()); + } + return jobNames; + } catch (Exception e) { + throw newPlainException(e); + } + } + + public String getJobStoreClassName() { + return scheduler.getJobStoreClass().getName(); + } + + public Set getPausedTriggerGroups() throws Exception { + try { + return scheduler.getPausedTriggerGroups(); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public CompositeData getTrigger(String name, String groupName) throws Exception { + try { + Trigger trigger = scheduler.getTrigger(triggerKey(name, groupName)); + return TriggerSupport.toCompositeData(trigger); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public List getTriggerGroupNames() throws Exception { + try { + return scheduler.getTriggerGroupNames(); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public List getTriggerNames(String groupName) throws Exception { + try { + List triggerNames = new ArrayList(); + for(TriggerKey key: scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(groupName))) { + triggerNames.add(key.getName()); + } + return triggerNames; + } catch (Exception e) { + throw newPlainException(e); + } + } + + public String getTriggerState(String triggerName, String triggerGroupName) throws Exception { + try { + TriggerKey triggerKey = triggerKey(triggerName, triggerGroupName); + TriggerState ts = scheduler.getTriggerState(triggerKey); + return ts.name(); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public List getTriggersOfJob(String jobName, String jobGroupName) throws Exception { + try { + JobKey jobKey = jobKey(jobName, jobGroupName); + return TriggerSupport.toCompositeList(scheduler.getTriggersOfJob(jobKey)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public boolean interruptJob(String jobName, String jobGroupName) throws Exception { + try { + return scheduler.interrupt(jobKey(jobName, jobGroupName)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public boolean interruptJob(String fireInstanceId) throws Exception { + try { + return scheduler.interrupt(fireInstanceId); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public Date scheduleJob(String jobName, String jobGroup, + String triggerName, String triggerGroup) throws Exception { + try { + JobKey jobKey = jobKey(jobName, jobGroup); + JobDetail jobDetail = scheduler.getJobDetail(jobKey); + if (jobDetail == null) { + throw new IllegalArgumentException("No such job: " + jobKey); + } + TriggerKey triggerKey = triggerKey(triggerName, triggerGroup); + Trigger trigger = scheduler.getTrigger(triggerKey); + if (trigger == null) { + throw new IllegalArgumentException("No such trigger: " + triggerKey); + } + return scheduler.scheduleJob(jobDetail, trigger); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public boolean unscheduleJob(String triggerName, String triggerGroup) throws Exception { + try { + return scheduler.unscheduleJob(triggerKey(triggerName, triggerGroup)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void clear() throws Exception { + try { + scheduler.clear(); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public String getVersion() { + return scheduler.getVersion(); + } + + public boolean isShutdown() { + return scheduler.isShutdown(); + } + + public boolean isStarted() { + return scheduler.isStarted(); + } + + public void start() throws Exception { + try { + scheduler.start(); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void shutdown() { + scheduler.shutdown(); + } + + public void standby() { + scheduler.standby(); + } + + public boolean isStandbyMode() { + return scheduler.isInStandbyMode(); + } + + public String getSchedulerName() { + return scheduler.getSchedulerName(); + } + + public String getSchedulerInstanceId() { + return scheduler.getSchedulerInstanceId(); + } + + public String getThreadPoolClassName() { + return scheduler.getThreadPoolClass().getName(); + } + + public int getThreadPoolSize() { + return scheduler.getThreadPoolSize(); + } + + public void pauseJob(String jobName, String jobGroup) throws Exception { + try { + scheduler.pauseJob(jobKey(jobName, jobGroup)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void pauseJobs(GroupMatcher matcher) throws Exception { + try { + scheduler.pauseJobs(matcher); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void pauseJobGroup(String jobGroup) throws Exception { + pauseJobs(GroupMatcher.groupEquals(jobGroup)); + } + + public void pauseJobsStartingWith(String jobGroupPrefix) throws Exception { + pauseJobs(GroupMatcher.groupStartsWith(jobGroupPrefix)); + } + + public void pauseJobsEndingWith(String jobGroupSuffix) throws Exception { + pauseJobs(GroupMatcher.groupEndsWith(jobGroupSuffix)); + } + + public void pauseJobsContaining(String jobGroupToken) throws Exception { + pauseJobs(GroupMatcher.groupContains(jobGroupToken)); + } + + public void pauseJobsAll() throws Exception { + pauseJobs(GroupMatcher.anyJobGroup()); + } + + public void pauseAllTriggers() throws Exception { + try { + scheduler.pauseAll(); + } catch (Exception e) { + throw newPlainException(e); + } + } + + private void pauseTriggers(GroupMatcher matcher) throws Exception { + try { + scheduler.pauseTriggers(matcher); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void pauseTriggerGroup(String triggerGroup) throws Exception { + pauseTriggers(GroupMatcher.groupEquals(triggerGroup)); + } + + public void pauseTriggersStartingWith(String triggerGroupPrefix) throws Exception { + pauseTriggers(GroupMatcher.groupStartsWith(triggerGroupPrefix)); + } + + public void pauseTriggersEndingWith(String triggerGroupSuffix) throws Exception { + pauseTriggers(GroupMatcher.groupEndsWith(triggerGroupSuffix)); + } + + public void pauseTriggersContaining(String triggerGroupToken) throws Exception { + pauseTriggers(GroupMatcher.groupContains(triggerGroupToken)); + } + + public void pauseTriggersAll() throws Exception { + pauseTriggers(GroupMatcher.anyTriggerGroup()); + } + + public void pauseTrigger(String triggerName, String triggerGroup) throws Exception { + try { + scheduler.pauseTrigger(triggerKey(triggerName, triggerGroup)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void resumeAllTriggers() throws Exception { + try { + scheduler.resumeAll(); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void resumeJob(String jobName, String jobGroup) throws Exception { + try { + scheduler.resumeJob(jobKey(jobName, jobGroup)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void resumeJobs(GroupMatcher matcher) throws Exception { + try { + scheduler.resumeJobs(matcher); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void resumeJobGroup(String jobGroup) throws Exception { + resumeJobs(GroupMatcher.groupEquals(jobGroup)); + } + + public void resumeJobsStartingWith(String jobGroupPrefix) throws Exception { + resumeJobs(GroupMatcher.groupStartsWith(jobGroupPrefix)); + } + + public void resumeJobsEndingWith(String jobGroupSuffix) throws Exception { + resumeJobs(GroupMatcher.groupEndsWith(jobGroupSuffix)); + } + + public void resumeJobsContaining(String jobGroupToken) throws Exception { + resumeJobs(GroupMatcher.groupContains(jobGroupToken)); + } + + public void resumeJobsAll() throws Exception { + resumeJobs(GroupMatcher.anyJobGroup()); + } + + public void resumeTrigger(String triggerName, String triggerGroup) throws Exception { + try { + scheduler.resumeTrigger(triggerKey(triggerName, triggerGroup)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + private void resumeTriggers(GroupMatcher matcher) throws Exception { + try { + scheduler.resumeTriggers(matcher); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void resumeTriggerGroup(String triggerGroup) throws Exception { + resumeTriggers(GroupMatcher.groupEquals(triggerGroup)); + } + + public void resumeTriggersStartingWith(String triggerGroupPrefix) throws Exception { + resumeTriggers(GroupMatcher.groupStartsWith(triggerGroupPrefix)); + } + + public void resumeTriggersEndingWith(String triggerGroupSuffix) throws Exception { + resumeTriggers(GroupMatcher.groupEndsWith(triggerGroupSuffix)); + } + + public void resumeTriggersContaining(String triggerGroupToken) throws Exception { + resumeTriggers(GroupMatcher.groupContains(triggerGroupToken)); + } + + public void resumeTriggersAll() throws Exception { + resumeTriggers(GroupMatcher.anyTriggerGroup()); + } + + public void triggerJob(String jobName, String jobGroup, Map jobDataMap) + throws Exception { + try { + scheduler.triggerJob(jobKey(jobName, jobGroup), new JobDataMap(jobDataMap)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + public void triggerJob(CompositeData trigger) throws Exception { + try { + scheduler.triggerJob(TriggerSupport.newTrigger(trigger)); + } catch (Exception e) { + throw newPlainException(e); + } + } + + // ScheduleListener + + public void jobAdded(JobDetail jobDetail) { + sendNotification(JOB_ADDED, JobDetailSupport.toCompositeData(jobDetail)); + } + + public void jobDeleted(JobKey jobKey) { + Map map = new HashMap(); + map.put("jobName", jobKey.getName()); + map.put("jobGroup", jobKey.getGroup()); + sendNotification(JOB_DELETED, map); + } + + public void jobScheduled(Trigger trigger) { + sendNotification(JOB_SCHEDULED, TriggerSupport.toCompositeData(trigger)); + } + + public void jobUnscheduled(TriggerKey triggerKey) { + Map map = new HashMap(); + map.put("triggerName", triggerKey.getName()); + map.put("triggerGroup", triggerKey.getGroup()); + sendNotification(JOB_UNSCHEDULED, map); + } + + public void schedulingDataCleared() { + sendNotification(SCHEDULING_DATA_CLEARED); + } + + public void jobPaused(JobKey jobKey) { + Map map = new HashMap(); + map.put("jobName", jobKey.getName()); + map.put("jobGroup", jobKey.getGroup()); + sendNotification(JOBS_PAUSED, map); + } + + public void jobsPaused(String jobGroup) { + Map map = new HashMap(); + map.put("jobName", null); + map.put("jobGroup", jobGroup); + sendNotification(JOBS_PAUSED, map); + } + + public void jobsResumed(String jobGroup) { + Map map = new HashMap(); + map.put("jobName", null); + map.put("jobGroup", jobGroup); + sendNotification(JOBS_RESUMED, map); + } + + public void jobResumed(JobKey jobKey) { + Map map = new HashMap(); + map.put("jobName", jobKey.getName()); + map.put("jobGroup", jobKey.getGroup()); + sendNotification(JOBS_RESUMED, map); + } + + public void schedulerError(String msg, SchedulerException cause) { + sendNotification(SCHEDULER_ERROR, cause.getMessage()); + } + + public void schedulerStarted() { + sendNotification(SCHEDULER_STARTED); + } + + //not doing anything, just like schedulerShuttingdown + public void schedulerStarting() { + } + + public void schedulerInStandbyMode() { + sendNotification(SCHEDULER_PAUSED); + } + + public void schedulerShutdown() { + scheduler.removeInternalSchedulerListener(this); + scheduler.removeInternalJobListener(getName()); + + sendNotification(SCHEDULER_SHUTDOWN); + } + + public void schedulerShuttingdown() { + } + + public void triggerFinalized(Trigger trigger) { + Map map = new HashMap(); + map.put("triggerName", trigger.getKey().getName()); + map.put("triggerGroup", trigger.getKey().getGroup()); + sendNotification(TRIGGER_FINALIZED, map); + } + + public void triggersPaused(String triggerGroup) { + Map map = new HashMap(); + map.put("triggerName", null); + map.put("triggerGroup", triggerGroup); + sendNotification(TRIGGERS_PAUSED, map); + } + + public void triggerPaused(TriggerKey triggerKey) { + Map map = new HashMap(); + if(triggerKey != null) { + map.put("triggerName", triggerKey.getName()); + map.put("triggerGroup", triggerKey.getGroup()); + } + sendNotification(TRIGGERS_PAUSED, map); + } + + public void triggersResumed(String triggerGroup) { + Map map = new HashMap(); + map.put("triggerName", null); + map.put("triggerGroup", triggerGroup); + sendNotification(TRIGGERS_RESUMED, map); + } + + public void triggerResumed(TriggerKey triggerKey) { + Map map = new HashMap(); + if(triggerKey != null) { + map.put("triggerName", triggerKey.getName()); + map.put("triggerGroup", triggerKey.getGroup()); + } + sendNotification(TRIGGERS_RESUMED, map); + } + + // JobListener + + public String getName() { + return "QuartzSchedulerMBeanImpl.listener"; + } + + public void jobExecutionVetoed(JobExecutionContext context) { + try { + sendNotification(JOB_EXECUTION_VETOED, JobExecutionContextSupport + .toCompositeData(context)); + } catch (Exception e) { + throw new RuntimeException(newPlainException(e)); + } + } + + public void jobToBeExecuted(JobExecutionContext context) { + try { + sendNotification(JOB_TO_BE_EXECUTED, JobExecutionContextSupport + .toCompositeData(context)); + } catch (Exception e) { + throw new RuntimeException(newPlainException(e)); + } + } + + public void jobWasExecuted(JobExecutionContext context, + JobExecutionException jobException) { + try { + sendNotification(JOB_WAS_EXECUTED, JobExecutionContextSupport + .toCompositeData(context)); + } catch (Exception e) { + throw new RuntimeException(newPlainException(e)); + } + } + + // NotificationBroadcaster + + /** + * sendNotification + * + * @param eventType + */ + public void sendNotification(String eventType) { + sendNotification(eventType, null, null); + } + + /** + * sendNotification + * + * @param eventType + * @param data + */ + public void sendNotification(String eventType, Object data) { + sendNotification(eventType, data, null); + } + + /** + * sendNotification + * + * @param eventType + * @param data + * @param msg + */ + public void sendNotification(String eventType, Object data, String msg) { + Notification notif = new Notification(eventType, this, sequenceNumber + .incrementAndGet(), System.currentTimeMillis(), msg); + if (data != null) { + notif.setUserData(data); + } + emitter.sendNotification(notif); + } + + /** + * @author gkeim + */ + private class Emitter extends NotificationBroadcasterSupport { + /** + * @see javax.management.NotificationBroadcasterSupport#getNotificationInfo() + */ + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + return QuartzSchedulerMBeanImpl.this.getNotificationInfo(); + } + } + + /** + * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, + * javax.management.NotificationFilter, java.lang.Object) + */ + public void addNotificationListener(NotificationListener notif, + NotificationFilter filter, Object callBack) { + emitter.addNotificationListener(notif, filter, callBack); + } + + /** + * @see javax.management.NotificationBroadcaster#getNotificationInfo() + */ + public MBeanNotificationInfo[] getNotificationInfo() { + return NOTIFICATION_INFO; + } + + /** + * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener) + */ + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + emitter.removeNotificationListener(listener); + } + + /** + * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, + * javax.management.NotificationFilter, java.lang.Object) + */ + public void removeNotificationListener(NotificationListener notif, + NotificationFilter filter, Object callBack) + throws ListenerNotFoundException { + emitter.removeNotificationListener(notif, filter, callBack); + } + + public synchronized boolean isSampledStatisticsEnabled() { + return sampledStatisticsEnabled; + } + + public void setSampledStatisticsEnabled(boolean enabled) { + if (enabled != this.sampledStatisticsEnabled) { + this.sampledStatisticsEnabled = enabled; + if(enabled) { + this.sampledStatistics = new SampledStatisticsImpl(scheduler); + } + else { + this.sampledStatistics.shutdown(); + this.sampledStatistics = NULL_SAMPLED_STATISTICS; + } + sendNotification(SAMPLED_STATISTICS_ENABLED, Boolean.valueOf(enabled)); + } + } + + public long getJobsCompletedMostRecentSample() { + return this.sampledStatistics.getJobsCompletedMostRecentSample(); + } + + public long getJobsExecutedMostRecentSample() { + return this.sampledStatistics.getJobsExecutingMostRecentSample(); + } + + public long getJobsScheduledMostRecentSample() { + return this.sampledStatistics.getJobsScheduledMostRecentSample(); + } + + public Map getPerformanceMetrics() { + Map result = new HashMap(); + result.put("JobsCompleted", Long + .valueOf(getJobsCompletedMostRecentSample())); + result.put("JobsExecuted", Long + .valueOf(getJobsExecutedMostRecentSample())); + result.put("JobsScheduled", Long + .valueOf(getJobsScheduledMostRecentSample())); + return result; + } +} Index: 3rdParty_sources/quartz/org/quartz/core/QuartzSchedulerResources.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/core/QuartzSchedulerResources.java (.../QuartzSchedulerResources.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/core/QuartzSchedulerResources.java (.../QuartzSchedulerResources.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,12 +16,15 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.core; +import java.util.ArrayList; +import java.util.List; + +import org.quartz.management.ManagementRESTServiceConfiguration; import org.quartz.spi.JobStore; +import org.quartz.spi.SchedulerPlugin; +import org.quartz.spi.ThreadExecutor; import org.quartz.spi.ThreadPool; /** @@ -70,6 +73,31 @@ private JobRunShellFactory jobRunShellFactory; + private List schedulerPlugins = new ArrayList(10); + + private boolean makeSchedulerThreadDaemon = false; + + private boolean threadsInheritInitializersClassLoadContext = false; + + private String rmiBindName; + + private boolean jmxExport; + + private String jmxObjectName; + + private ManagementRESTServiceConfiguration managementRESTServiceConfiguration; + + private ThreadExecutor threadExecutor; + + private boolean runUpdateCheck = true; + + private long batchTimeWindow = 0; + + private int maxBatchSize = 1; + + private boolean interruptJobsOnShutdown = false; + private boolean interruptJobsOnShutdownWithWait = false; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -113,9 +141,10 @@ * if name is null or empty. */ public void setName(String name) { - if (name == null || name.trim().length() == 0) - throw new IllegalArgumentException( - "Scheduler name cannot be empty."); + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException( + "Scheduler name cannot be empty."); + } this.name = name; @@ -143,9 +172,10 @@ * if name is null or empty. */ public void setInstanceId(String instanceId) { - if (instanceId == null || instanceId.trim().length() == 0) - throw new IllegalArgumentException( - "Scheduler instanceId cannot be empty."); + if (instanceId == null || instanceId.trim().length() == 0) { + throw new IllegalArgumentException( + "Scheduler instanceId cannot be empty."); + } this.instanceId = instanceId; } @@ -246,9 +276,10 @@ * if name is null or empty. */ public void setThreadName(String threadName) { - if (threadName == null || threadName.trim().length() == 0) - throw new IllegalArgumentException( - "Scheduler thread name cannot be empty."); + if (threadName == null || threadName.trim().length() == 0) { + throw new IllegalArgumentException( + "Scheduler thread name cannot be empty."); + } this.threadName = threadName; } @@ -264,19 +295,23 @@ */ 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 + || 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; } @@ -301,8 +336,9 @@ * if threadPool is null. */ public void setThreadPool(ThreadPool threadPool) { - if (threadPool == null) - throw new IllegalArgumentException("ThreadPool cannot be null."); + if (threadPool == null) { + throw new IllegalArgumentException("ThreadPool cannot be null."); + } this.threadPool = threadPool; } @@ -327,8 +363,9 @@ * if jobStore is null. */ public void setJobStore(JobStore jobStore) { - if (jobStore == null) - throw new IllegalArgumentException("JobStore cannot be null."); + if (jobStore == null) { + throw new IllegalArgumentException("JobStore cannot be null."); + } this.jobStore = jobStore; } @@ -353,11 +390,206 @@ * if jobRunShellFactory is null. */ public void setJobRunShellFactory(JobRunShellFactory jobRunShellFactory) { - if (jobRunShellFactory == null) - throw new IllegalArgumentException( - "JobRunShellFactory cannot be null."); + if (jobRunShellFactory == null) { + throw new IllegalArgumentException( + "JobRunShellFactory cannot be null."); + } this.jobRunShellFactory = jobRunShellFactory; } + /** + *

+ * Add the given {@link 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). + *

+ */ + public void addSchedulerPlugin(SchedulerPlugin plugin) { + schedulerPlugins.add(plugin); + } + + /** + *

+ * Get the List of all + * {@link org.quartz.spi.SchedulerPlugin}s for the + * {@link QuartzScheduler} to use. + *

+ */ + public List getSchedulerPlugins() { + return schedulerPlugins; + } + + /** + * Get whether to mark the Quartz scheduling thread as daemon. + * + * @see Thread#setDaemon(boolean) + */ + public boolean getMakeSchedulerThreadDaemon() { + return makeSchedulerThreadDaemon; + } + + /** + * Set whether to mark the Quartz scheduling thread as daemon. + * + * @see Thread#setDaemon(boolean) + */ + public void setMakeSchedulerThreadDaemon(boolean makeSchedulerThreadDaemon) { + this.makeSchedulerThreadDaemon = makeSchedulerThreadDaemon; + } + + /** + * 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 the name under which to bind the QuartzScheduler in RMI. Will + * return the value of the uniqueIdentifier property if explict RMI bind + * name was never set. + * + * @see #getUniqueIdentifier() + */ + public String getRMIBindName() { + return (rmiBindName == null) ? getUniqueIdentifier() : rmiBindName; + } + + /** + * Set the name under which to bind the QuartzScheduler in RMI. If unset, + * defaults to the value of the uniqueIdentifier property. + * + * @see #getUniqueIdentifier() + */ + public void setRMIBindName(String rmiBindName) { + this.rmiBindName = rmiBindName; + } + + /** + * Get whether the QuartzScheduler should be registered with the local + * MBeanServer. + */ + public boolean getJMXExport() { + return jmxExport; + } + + /** + * Set whether the QuartzScheduler should be registered with the local + * MBeanServer. + */ + public void setJMXExport(boolean jmxExport) { + this.jmxExport = jmxExport; + } + + /** + * Get 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 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; + } + + /** + * Get the ThreadExecutor which runs the QuartzSchedulerThread + */ + public ThreadExecutor getThreadExecutor() { + return threadExecutor; + } + + /** + * Set the ThreadExecutor which runs the QuartzSchedulerThread + */ + public void setThreadExecutor(ThreadExecutor threadExecutor) { + this.threadExecutor = threadExecutor; + } + + /** + * 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.replaceAll(":|=|\n", ".") + + ",instance=" + schedInstId; + } + + public boolean isRunUpdateCheck() { + return runUpdateCheck; + } + + public void setRunUpdateCheck(boolean runUpdateCheck) { + this.runUpdateCheck = runUpdateCheck; + } + + public long getBatchTimeWindow() { + return batchTimeWindow; + } + + public void setBatchTimeWindow(long batchTimeWindow) { + this.batchTimeWindow = batchTimeWindow; + } + + public int getMaxBatchSize() { + return maxBatchSize; + } + + public void setMaxBatchSize(int maxBatchSize) { + this.maxBatchSize = maxBatchSize; + } + + public boolean isInterruptJobsOnShutdown() { + return interruptJobsOnShutdown; + } + + public void setInterruptJobsOnShutdown(boolean interruptJobsOnShutdown) { + this.interruptJobsOnShutdown = interruptJobsOnShutdown; + } + + public boolean isInterruptJobsOnShutdownWithWait() { + return interruptJobsOnShutdownWithWait; + } + + public void setInterruptJobsOnShutdownWithWait( + boolean interruptJobsOnShutdownWithWait) { + this.interruptJobsOnShutdownWithWait = interruptJobsOnShutdownWithWait; + } + + + public ManagementRESTServiceConfiguration getManagementRESTServiceConfiguration() { + return managementRESTServiceConfiguration; + } + + public void setManagementRESTServiceConfiguration(ManagementRESTServiceConfiguration managementRESTServiceConfiguration) { + this.managementRESTServiceConfiguration = managementRESTServiceConfiguration; + } + } Index: 3rdParty_sources/quartz/org/quartz/core/QuartzSchedulerThread.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/core/QuartzSchedulerThread.java (.../QuartzSchedulerThread.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/core/QuartzSchedulerThread.java (.../QuartzSchedulerThread.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,72 +1,71 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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.core; +import java.util.ArrayList; +import java.util.List; import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.quartz.Job; import org.quartz.JobPersistenceException; import org.quartz.SchedulerException; import org.quartz.Trigger; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.spi.OperableTrigger; import org.quartz.spi.TriggerFiredBundle; +import org.quartz.spi.TriggerFiredResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** *

* The thread responsible for performing the work of firing {@link Trigger} * s that are registered with the {@link QuartzScheduler}. *

- * + * * @see QuartzScheduler - * @see Job + * @see org.quartz.Job * @see Trigger - * + * * @author James House */ public class QuartzSchedulerThread extends Thread { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Data members. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ private QuartzScheduler qs; private QuartzSchedulerResources qsRsrcs; - private Object pauseLock = new Object(); + private final Object sigLock = new Object(); - private Object idleLock = new Object(); - private boolean signaled; + private long signaledNextFireTime; private boolean paused; - private boolean halted; + private AtomicBoolean 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 @@ -77,13 +76,13 @@ private int idleWaitVariablness = 7 * 1000; - private long dbFailureRetryInterval = 15L * 1000L; + private final Logger log = LoggerFactory.getLogger(getClass()); /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Constructors. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -94,9 +93,8 @@ * with normal priority. *

*/ - QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs, - SchedulingContext ctxt) { - this(qs, qsRsrcs, ctxt, false, Thread.NORM_PRIORITY); + QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs) { + this(qs, qsRsrcs, qsRsrcs.getMakeSchedulerThreadDaemon(), Thread.NORM_PRIORITY); } /** @@ -106,28 +104,30 @@ * attributes. *

*/ - QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs, - SchedulingContext ctxt, boolean setDaemon, int threadPrio) { + QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs, 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(); + halted = new AtomicBoolean(false); } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Interface. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -136,14 +136,6 @@ 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); } @@ -154,13 +146,13 @@ *

*/ void togglePause(boolean pause) { - synchronized (pauseLock) { + synchronized (sigLock) { paused = pause; if (paused) { - signalSchedulingChange(); + signalSchedulingChange(0); } else { - pauseLock.notify(); + sigLock.notifyAll(); } } } @@ -170,16 +162,34 @@ * Signals the main processing loop to pause at the next possible point. *

*/ - void halt() { - synchronized (pauseLock) { - halted = true; + void halt(boolean wait) { + synchronized (sigLock) { + halted.set(true); if (paused) { - pauseLock.notify(); + sigLock.notifyAll(); } else { - signalSchedulingChange(); + signalSchedulingChange(0); } } + + if (wait) { + boolean interrupted = false; + try { + while (true) { + try { + join(); + break; + } catch (InterruptedException _) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } } boolean isPaused() { @@ -192,287 +202,290 @@ * made - in order to interrupt any sleeping that may be occuring while * waiting for the fire time to arrive. *

+ * + * @param candidateNewNextFireTime 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). */ - void signalSchedulingChange() { - signaled = true; + 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. *

*/ + @Override public void run() { boolean lastAcquireFailed = false; - - while (!halted) { - - signaled = false; - - try { - // check if we're supposed to pause... - synchronized (pauseLock) { - while (paused && !halted) { - try { - // wait until togglePause(false) is called... - pauseLock.wait(100L); - } catch (InterruptedException ignore) { + + while (!halted.get()) { + try { + // check if we're supposed to pause... + synchronized (sigLock) { + while (paused && !halted.get()) { + try { + // wait until togglePause(false) is called... + sigLock.wait(1000L); + } catch (InterruptedException ignore) { + } } - } - if (halted) { - break; + if (halted.get()) { + break; + } } - } - Trigger trigger = null; + int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads(); + if(availThreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads... - long now = System.currentTimeMillis(); + List triggers = null; - 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; - } + long now = System.currentTimeMillis(); - if (trigger != null) { - - now = System.currentTimeMillis(); - long triggerTime = trigger.getNextFireTime().getTime(); - long timeUntilTrigger = triggerTime - now; - long spinInterval = 10; - - // this looping may seem a bit silly, but it's the - // current work-around - // for a dead-lock that can occur if the Thread.sleep() - // is replaced with - // a obj.wait() that gets notified when the signal is - // set... - // so to be able to detect the signal change without - // sleeping the entire - // timeUntilTrigger, we spin here... don't worry - // though, this spinning - // doesn't even register 0.2% cpu usage on a pentium 4. - int numPauses = (int) (timeUntilTrigger / spinInterval); - while (numPauses >= 0 && !signaled) { - + clearSignaledSchedulingChange(); try { - Thread.sleep(spinInterval); - } catch (InterruptedException ignore) { - } - - now = System.currentTimeMillis(); - timeUntilTrigger = triggerTime - now; - numPauses = (int) (timeUntilTrigger / spinInterval); - } - if (signaled) { - try { - qsRsrcs.getJobStore().releaseAcquiredTrigger( - ctxt, trigger); + triggers = qsRsrcs.getJobStore().acquireNextTriggers( + now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow()); + lastAcquireFailed = false; + if (log.isDebugEnabled()) + log.debug("batch acquisition of " + (triggers == null ? 0 : triggers.size()) + " triggers"); } catch (JobPersistenceException jpe) { - qs.notifySchedulerListenersError( - "An error occured while releasing trigger '" - + trigger.getFullName() + "'", + if(!lastAcquireFailed) { + qs.notifySchedulerListenersError( + "An error occurred while scanning for the next triggers to fire.", jpe); - // db connection must have failed... keep - // retrying until it's up... - releaseTriggerRetryLoop(trigger); + } + lastAcquireFailed = true; + continue; } catch (RuntimeException e) { - getLog().error( - "releaseTriggerRetryLoop: RuntimeException " - +e.getMessage(), e); - // db connection must have failed... keep - // retrying until it's up... - releaseTriggerRetryLoop(trigger); + if(!lastAcquireFailed) { + getLog().error("quartzSchedulerThreadLoop: RuntimeException " + +e.getMessage(), e); + } + lastAcquireFailed = true; + continue; } - signaled = false; - continue; - } - // set trigger to 'executing' - TriggerFiredBundle bndle = null; + if (triggers != null && !triggers.isEmpty()) { - 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); - } + now = System.currentTimeMillis(); + long triggerTime = triggers.get(0).getNextFireTime().getTime(); + long timeUntilTrigger = triggerTime - now; + while(timeUntilTrigger > 2) { + synchronized (sigLock) { + if (halted.get()) { + break; + } + if (!isCandidateNewTimeEarlierWithinReason(triggerTime, false)) { + 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(releaseIfScheduleChangedSignificantly(triggers, triggerTime)) { + break; + } + now = System.currentTimeMillis(); + timeUntilTrigger = triggerTime - now; + } - // it's possible to get 'null' if the trigger was paused, - // blocked, or other similar occurances that prevent it being - // fired at this time... - 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; - } + // this happens if releaseIfScheduleChangedSignificantly decided to release triggers + if(triggers.isEmpty()) + continue; - // TODO: improvements: - // - // 1- get thread from pool before firing trigger. - // 2- make sure we can get a job runshell first as well, or - // don't let that throw an exception (right now it never does, - // bugthe signature says it can). - // 3- acquire more triggers at a time (based on num threads?) - - - 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 releasing trigger '" - + trigger.getFullName() + "'", se2); - // db connection must have failed... keep retrying - // until it's up... - errorTriggerRetryLoop(bndle); - } - continue; - } + // set triggers to 'executing' + List bndles = new ArrayList(); - qsRsrcs.getThreadPool().runInThread(shell); + boolean goAhead = true; + synchronized(sigLock) { + goAhead = !halted.get(); + } + if(goAhead) { + try { + List res = qsRsrcs.getJobStore().triggersFired(triggers); + if(res != null) + bndles = res; + } catch (SchedulerException se) { + qs.notifySchedulerListenersError( + "An error occurred while firing triggers '" + + triggers + "'", se); + //QTZ-179 : a problem occurred interacting with the triggers from the db + //we release them and loop again + for (int i = 0; i < triggers.size(); i++) { + qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i)); + } + continue; + } - continue; - } + } - // this looping may seem a bit silly, but it's the current - // work-around - // for a dead-lock that can occur if the Thread.sleep() is replaced - // with - // a obj.wait() that gets notified when the signal is set... - // so to be able to detect the signal change without sleeping the - // entier - // getRandomizedIdleWaitTime(), we spin here... don't worry though, - // the - // CPU usage of this spinning can't even be measured on a pentium - // 4. - now = System.currentTimeMillis(); - long waitTime = now + getRandomizedIdleWaitTime(); - long timeUntilContinue = waitTime - now; - long spinInterval = 10; - int numPauses = (int) (timeUntilContinue / spinInterval); + for (int i = 0; i < bndles.size(); i++) { + TriggerFiredResult result = bndles.get(i); + TriggerFiredBundle bndle = result.getTriggerFiredBundle(); + Exception exception = result.getException(); - while (numPauses > 0 && !signaled) { + if (exception instanceof RuntimeException) { + getLog().error("RuntimeException while firing trigger " + triggers.get(i), exception); + qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i)); + continue; + } - try { - Thread.sleep(10L); - } catch (InterruptedException ignore) { + // it's possible to get 'null' if the triggers 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) { + qsRsrcs.getJobStore().releaseAcquiredTrigger(triggers.get(i)); + continue; + } + + JobRunShell shell = null; + try { + shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle); + shell.initialize(qs); + } catch (SchedulerException se) { + qsRsrcs.getJobStore().triggeredJobComplete(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR); + continue; + } + + if (qsRsrcs.getThreadPool().runInThread(shell) == false) { + // 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(triggers.get(i), bndle.getJobDetail(), CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR); + } + + } + + continue; // while (!halted) + } + } else { // if(availThreadCount > 0) + // should never happen, if threadPool.blockForAvailableThreads() follows contract + continue; // while (!halted) } - now = System.currentTimeMillis(); - timeUntilContinue = waitTime - now; - numPauses = (int) (timeUntilContinue / spinInterval); + long now = System.currentTimeMillis(); + long waitTime = now + getRandomizedIdleWaitTime(); + long timeUntilContinue = waitTime - now; + synchronized(sigLock) { + try { + if(!halted.get()) { + // QTZ-336 A job might have been completed in the mean time and we might have + // missed the scheduled changed signal by not waiting for the notify() yet + // Check that before waiting for too long in case this very job needs to be + // scheduled very soon + if (!isScheduleChanged()) { + sigLock.wait(timeUntilContinue); + } + } + } catch (InterruptedException ignore) { + } + } + + } catch(RuntimeException re) { + getLog().error("Runtime error occurred in main trigger firing loop.", re); } - } - catch(RuntimeException re) { - getLog().error("Runtime error occured in main trigger firing loop.", re); - } - } // loop... + } // while (!halted) // drop references to scheduler stuff to aid garbage collection... qs = null; qsRsrcs = null; } - 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); - } + private boolean releaseIfScheduleChangedSignificantly( + List triggers, long triggerTime) { + if (isCandidateNewTimeEarlierWithinReason(triggerTime, true)) { + // above call does a clearSignaledSchedulingChange() + for (OperableTrigger trigger : triggers) { + qsRsrcs.getJobStore().releaseAcquiredTrigger(trigger); } - } finally { - if(retryCount == 0) - getLog().info("releaseTriggerRetryLoop: connection restored."); + triggers.clear(); + return true; } + return false; } - - 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); - } + + private boolean isCandidateNewTimeEarlierWithinReason(long oldTime, boolean clearSignal) { + + // 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) { + + if (!isScheduleChanged()) + return false; + + 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? + long diff = oldTime - System.currentTimeMillis(); + if(diff < (qsRsrcs.getJobStore().supportsPersistence() ? 70L : 7L)) + earlier = false; } - } finally { - if(retryCount == 0) - getLog().info("releaseTriggerRetryLoop: connection restored."); + + if(clearSignal) { + clearSignaledSchedulingChange(); + } + + return earlier; } } - - public static Log getLog() { - return LogFactory.getLog(QuartzSchedulerThread.class); + + public Logger getLog() { + return log; } } // end of QuartzSchedulerThread Index: 3rdParty_sources/quartz/org/quartz/core/RemotableQuartzScheduler.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/core/RemotableQuartzScheduler.java (.../RemotableQuartzScheduler.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/core/RemotableQuartzScheduler.java (.../RemotableQuartzScheduler.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,27 +16,28 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.core; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Set; import org.quartz.Calendar; import org.quartz.JobDataMap; import org.quartz.JobDetail; -import org.quartz.JobListener; +import org.quartz.JobExecutionContext; +import org.quartz.JobKey; import org.quartz.SchedulerContext; import org.quartz.SchedulerException; -import org.quartz.SchedulerListener; import org.quartz.Trigger; -import org.quartz.TriggerListener; +import org.quartz.TriggerKey; import org.quartz.UnableToInterruptJobException; +import org.quartz.Trigger.TriggerState; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.spi.OperableTrigger; /** * @author James House @@ -51,181 +52,124 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - public String getSchedulerName() throws RemoteException; + String getSchedulerName() throws RemoteException; - public String getSchedulerInstanceId() throws RemoteException; + String getSchedulerInstanceId() throws RemoteException; - public SchedulerContext getSchedulerContext() throws SchedulerException, - RemoteException; + SchedulerContext getSchedulerContext() throws SchedulerException, RemoteException; - public void start() throws SchedulerException, RemoteException; + void start() throws SchedulerException, RemoteException; - public void standby() throws RemoteException; + void startDelayed(int seconds) throws SchedulerException, RemoteException; + + void standby() throws RemoteException; - public boolean isInStandbyMode() throws RemoteException; + boolean isInStandbyMode() throws RemoteException; - public void shutdown() throws RemoteException; + void shutdown() throws RemoteException; - public void shutdown(boolean waitForJobsToComplete) throws RemoteException; + void shutdown(boolean waitForJobsToComplete) throws RemoteException; - public boolean isShutdown() throws RemoteException; + boolean isShutdown() throws RemoteException; - public Date runningSince() throws RemoteException; + Date runningSince() throws RemoteException; - public String getVersion() throws RemoteException; + String getVersion() throws RemoteException; - public int numJobsExecuted() throws RemoteException; + int numJobsExecuted() throws RemoteException; - public Class getJobStoreClass() throws RemoteException; + Class getJobStoreClass() throws RemoteException; - public boolean supportsPersistence() throws RemoteException; + boolean supportsPersistence() throws RemoteException; - public Class getThreadPoolClass() throws RemoteException; + boolean isClustered() throws RemoteException; - public int getThreadPoolSize() throws RemoteException; + Class getThreadPoolClass() throws RemoteException; - public List getCurrentlyExecutingJobs() throws SchedulerException, - RemoteException; + int getThreadPoolSize() throws RemoteException; - public Date scheduleJob(SchedulingContext ctxt, JobDetail jobDetail, - Trigger trigger) throws SchedulerException, RemoteException; - - public Date scheduleJob(SchedulingContext ctxt, Trigger trigger) - throws SchedulerException, RemoteException; - - public void addJob(SchedulingContext ctxt, JobDetail jobDetail, - boolean replace) throws SchedulerException, RemoteException; - - public boolean deleteJob(SchedulingContext ctxt, String jobName, - String groupName) throws SchedulerException, RemoteException; - - public boolean unscheduleJob(SchedulingContext ctxt, String triggerName, - String groupName) throws SchedulerException, RemoteException; - - public Date rescheduleJob(SchedulingContext ctxt, String triggerName, - String groupName, Trigger newTrigger) throws SchedulerException, RemoteException; - + void clear() throws SchedulerException, RemoteException; - public void triggerJob(SchedulingContext ctxt, String jobName, - String groupName, JobDataMap data) throws SchedulerException, RemoteException; + List getCurrentlyExecutingJobs() throws SchedulerException, RemoteException; - public void triggerJobWithVolatileTrigger(SchedulingContext ctxt, - String jobName, String groupName, JobDataMap data) throws SchedulerException, - RemoteException; + Date scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException, RemoteException; - public void pauseTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws SchedulerException, RemoteException; + Date scheduleJob(Trigger trigger) throws SchedulerException, RemoteException; - public void pauseTriggerGroup(SchedulingContext ctxt, String groupName) - throws SchedulerException, RemoteException; + void addJob(JobDetail jobDetail, boolean replace) throws SchedulerException, RemoteException; - public void pauseJob(SchedulingContext ctxt, String jobName, - String groupName) throws SchedulerException, RemoteException; + void addJob(JobDetail jobDetail, boolean replace, boolean storeNonDurableWhileAwaitingScheduling) throws SchedulerException, RemoteException; - public void pauseJobGroup(SchedulingContext ctxt, String groupName) - throws SchedulerException, RemoteException; + boolean deleteJob(JobKey jobKey) throws SchedulerException, RemoteException; - public void resumeTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws SchedulerException, RemoteException; + boolean unscheduleJob(TriggerKey triggerKey) throws SchedulerException, RemoteException; - public void resumeTriggerGroup(SchedulingContext ctxt, String groupName) - throws SchedulerException, RemoteException; + Date rescheduleJob(TriggerKey triggerKey, Trigger newTrigger) throws SchedulerException, RemoteException; + + void triggerJob(JobKey jobKey, JobDataMap data) throws SchedulerException, RemoteException; - public Set getPausedTriggerGroups(SchedulingContext ctxt) - throws SchedulerException, RemoteException; + void triggerJob(OperableTrigger trig) throws SchedulerException, RemoteException; - public void resumeJob(SchedulingContext ctxt, String jobName, - String groupName) throws SchedulerException, RemoteException; + void pauseTrigger(TriggerKey triggerKey) throws SchedulerException, RemoteException; - public void resumeJobGroup(SchedulingContext ctxt, String groupName) - throws SchedulerException, RemoteException; + void pauseTriggers(GroupMatcher matcher) throws SchedulerException, RemoteException; - public void pauseAll(SchedulingContext ctxt) throws SchedulerException, - RemoteException; + void pauseJob(JobKey jobKey) throws SchedulerException, RemoteException; - public void resumeAll(SchedulingContext ctxt) throws SchedulerException, - RemoteException; + void pauseJobs(GroupMatcher matcher) throws SchedulerException, RemoteException; - public String[] getJobGroupNames(SchedulingContext ctxt) - throws SchedulerException, RemoteException; + void resumeTrigger(TriggerKey triggerKey) throws SchedulerException, RemoteException; - public String[] getJobNames(SchedulingContext ctxt, String groupName) - throws SchedulerException, RemoteException; + void resumeTriggers(GroupMatcher matcher) throws SchedulerException, RemoteException; - public Trigger[] getTriggersOfJob(SchedulingContext ctxt, String jobName, - String groupName) throws SchedulerException, RemoteException; + Set getPausedTriggerGroups() throws SchedulerException, RemoteException; + + void resumeJob(JobKey jobKey) throws SchedulerException, RemoteException; - public String[] getTriggerGroupNames(SchedulingContext ctxt) - throws SchedulerException, RemoteException; + void resumeJobs(GroupMatcher matcher) throws SchedulerException, RemoteException; - public String[] getTriggerNames(SchedulingContext ctxt, String groupName) - throws SchedulerException, RemoteException; + void pauseAll() throws SchedulerException, RemoteException; - public JobDetail getJobDetail(SchedulingContext ctxt, String jobName, - String jobGroup) throws SchedulerException, RemoteException; + void resumeAll() throws SchedulerException, RemoteException; - public Trigger getTrigger(SchedulingContext ctxt, String triggerName, - String triggerGroup) throws SchedulerException, RemoteException; + List getJobGroupNames() throws SchedulerException, RemoteException; - public int getTriggerState(SchedulingContext ctxt, String triggerName, - String triggerGroup) throws SchedulerException, RemoteException; + Set getJobKeys(GroupMatcher matcher) throws SchedulerException, RemoteException; - public void addCalendar(SchedulingContext ctxt, String calName, - Calendar calendar, boolean replace, boolean updateTriggers) throws SchedulerException, - RemoteException; + List getTriggersOfJob(JobKey jobKey) throws SchedulerException, RemoteException; - public boolean deleteCalendar(SchedulingContext ctxt, String calName) - throws SchedulerException, RemoteException; + List getTriggerGroupNames() throws SchedulerException, RemoteException; - public Calendar getCalendar(SchedulingContext ctxt, String calName) - throws SchedulerException, RemoteException; + Set getTriggerKeys(GroupMatcher matcher) throws SchedulerException, RemoteException; - public String[] getCalendarNames(SchedulingContext ctxt) - throws SchedulerException, RemoteException; + JobDetail getJobDetail(JobKey jobKey) throws SchedulerException, RemoteException; - public void addGlobalJobListener(JobListener jobListener) - throws RemoteException; + Trigger getTrigger(TriggerKey triggerKey) throws SchedulerException, RemoteException; - public void addJobListener(JobListener jobListener) throws RemoteException; + TriggerState getTriggerState(TriggerKey triggerKey) throws SchedulerException, RemoteException; - public boolean removeGlobalJobListener(JobListener jobListener) - throws RemoteException; + void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) throws SchedulerException, RemoteException; - public boolean removeJobListener(String name) throws RemoteException; + boolean deleteCalendar(String calName) throws SchedulerException, RemoteException; - public List getGlobalJobListeners() throws RemoteException; + Calendar getCalendar(String calName) throws SchedulerException, RemoteException; - public Set getJobListenerNames() throws RemoteException; + List getCalendarNames() throws SchedulerException, RemoteException; - public JobListener getJobListener(String name) throws RemoteException; + boolean interrupt(JobKey jobKey) throws UnableToInterruptJobException,RemoteException; - public void addGlobalTriggerListener(TriggerListener triggerListener) - throws RemoteException; + boolean interrupt(String fireInstanceId) throws UnableToInterruptJobException,RemoteException; + + boolean checkExists(JobKey jobKey) throws SchedulerException,RemoteException; + + boolean checkExists(TriggerKey triggerKey) throws SchedulerException,RemoteException; + + public boolean deleteJobs(List jobKeys) throws SchedulerException,RemoteException; - public void addTriggerListener(TriggerListener triggerListener) - throws RemoteException; + public void scheduleJobs(Map> triggersAndJobs, boolean replace) throws SchedulerException,RemoteException; - public boolean removeGlobalTriggerListener(TriggerListener triggerListener) - throws RemoteException; + public void scheduleJob(JobDetail jobDetail, Set triggersForJob, boolean replace) throws SchedulerException,RemoteException; - public boolean removeTriggerListener(String name) throws RemoteException; - - public List getGlobalTriggerListeners() throws RemoteException; - - public Set getTriggerListenerNames() throws RemoteException; - - public TriggerListener getTriggerListener(String name) - throws RemoteException; - - public void addSchedulerListener(SchedulerListener schedulerListener) - throws RemoteException; - - public boolean removeSchedulerListener(SchedulerListener schedulerListener) - throws RemoteException; - - public List getSchedulerListeners() throws RemoteException; - - public boolean interrupt(SchedulingContext ctxt, String jobName, String groupName) throws UnableToInterruptJobException,RemoteException ; - - + public boolean unscheduleJobs(List triggerKeys) throws SchedulerException,RemoteException; + } Index: 3rdParty_sources/quartz/org/quartz/core/SampledStatistics.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/SampledStatistics.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/SampledStatistics.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,8 @@ +package org.quartz.core; + +public interface SampledStatistics { + long getJobsScheduledMostRecentSample(); + long getJobsExecutingMostRecentSample(); + long getJobsCompletedMostRecentSample(); + void shutdown(); +} Index: 3rdParty_sources/quartz/org/quartz/core/SampledStatisticsImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/SampledStatisticsImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/SampledStatisticsImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,109 @@ +package org.quartz.core; + +import java.util.Timer; + +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobListener; +import org.quartz.SchedulerListener; +import org.quartz.Trigger; +import org.quartz.listeners.SchedulerListenerSupport; +import org.quartz.utils.counter.CounterConfig; +import org.quartz.utils.counter.CounterManager; +import org.quartz.utils.counter.CounterManagerImpl; +import org.quartz.utils.counter.sampled.SampledCounter; +import org.quartz.utils.counter.sampled.SampledCounterConfig; +import org.quartz.utils.counter.sampled.SampledRateCounterConfig; + +public class SampledStatisticsImpl extends SchedulerListenerSupport implements SampledStatistics, JobListener, SchedulerListener { + @SuppressWarnings("unused") + private final QuartzScheduler scheduler; + + private static final String NAME = "QuartzSampledStatistics"; + + private static final int DEFAULT_HISTORY_SIZE = 30; + private static final int DEFAULT_INTERVAL_SECS = 1; + private final static SampledCounterConfig DEFAULT_SAMPLED_COUNTER_CONFIG = new SampledCounterConfig(DEFAULT_INTERVAL_SECS, + DEFAULT_HISTORY_SIZE, true, 0L); + @SuppressWarnings("unused") + private final static SampledRateCounterConfig DEFAULT_SAMPLED_RATE_COUNTER_CONFIG = new SampledRateCounterConfig(DEFAULT_INTERVAL_SECS, + DEFAULT_HISTORY_SIZE, true); + + private volatile CounterManager counterManager; + private final SampledCounter jobsScheduledCount; + private final SampledCounter jobsExecutingCount; + private final SampledCounter jobsCompletedCount; + + SampledStatisticsImpl(QuartzScheduler scheduler) { + this.scheduler = scheduler; + + counterManager = new CounterManagerImpl(new Timer(NAME+"Timer")); + jobsScheduledCount = createSampledCounter(DEFAULT_SAMPLED_COUNTER_CONFIG); + jobsExecutingCount = createSampledCounter(DEFAULT_SAMPLED_COUNTER_CONFIG); + jobsCompletedCount = createSampledCounter(DEFAULT_SAMPLED_COUNTER_CONFIG); + + scheduler.addInternalSchedulerListener(this); + scheduler.addInternalJobListener(this); + } + + public void shutdown() { + counterManager.shutdown(true); + } + + private SampledCounter createSampledCounter(CounterConfig defaultCounterConfig) { + return (SampledCounter) counterManager.createCounter(defaultCounterConfig); + } + + /** + * Clears the collected statistics. Resets all counters to zero + */ + public void clearStatistics() { + jobsScheduledCount.getAndReset(); + jobsExecutingCount.getAndReset(); + jobsCompletedCount.getAndReset(); + } + + public long getJobsCompletedMostRecentSample() { + return jobsCompletedCount.getMostRecentSample().getCounterValue(); + } + + public long getJobsExecutingMostRecentSample() { + return jobsExecutingCount.getMostRecentSample().getCounterValue(); + } + + public long getJobsScheduledMostRecentSample() { + return jobsScheduledCount.getMostRecentSample().getCounterValue(); + } + + public String getName() { + return NAME; + } + + @Override + public void jobScheduled(Trigger trigger) { + jobsScheduledCount.increment(); + } + + public void jobExecutionVetoed(JobExecutionContext context) { + /**/ + } + + public void jobToBeExecuted(JobExecutionContext context) { + jobsExecutingCount.increment(); + } + + public void jobWasExecuted(JobExecutionContext context, + JobExecutionException jobException) { + jobsCompletedCount.increment(); + } + + @Override + public void jobAdded(JobDetail jobDetail) { + /**/ + } + + public void jobDeleted(String jobName, String groupName) { + /**/ + } +} Index: 3rdParty_sources/quartz/org/quartz/core/SchedulerSignalerImpl.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/core/SchedulerSignalerImpl.java (.../SchedulerSignalerImpl.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/core/SchedulerSignalerImpl.java (.../SchedulerSignalerImpl.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,11 +16,11 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.core; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.quartz.JobKey; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.spi.SchedulerSignaler; @@ -33,6 +33,8 @@ */ public class SchedulerSignalerImpl implements SchedulerSignaler { + Logger log = LoggerFactory.getLogger(SchedulerSignalerImpl.class); + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -41,7 +43,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private QuartzScheduler sched; + protected QuartzScheduler sched; + protected QuartzSchedulerThread schedThread; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -51,31 +54,45 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - SchedulerSignalerImpl(QuartzScheduler sched) { + 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) { - QuartzScheduler.getLog().error( + sched.getLog().error( "Error notifying listeners of trigger misfire.", se); sched.notifySchedulerListenersError( "Error notifying listeners of trigger misfire.", se); } } - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ + public void notifySchedulerListenersFinalized(Trigger trigger) { + sched.notifySchedulerListenersFinalized(trigger); + } - public void signalSchedulingChange() { - sched.notifySchedulerThread(); + public void signalSchedulingChange(long candidateNewNextFireTime) { + schedThread.signalSchedulingChange(candidateNewNextFireTime); } + public void notifySchedulerListenersJobDeleted(JobKey jobKey) { + sched.notifySchedulerListenersJobDeleted(jobKey); + } + + public void notifySchedulerListenersError(String string, SchedulerException jpe) { + sched.notifySchedulerListenersError(string, jpe); + } } Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/core/SchedulingContext.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/core/jmx/CronTriggerSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/jmx/CronTriggerSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/jmx/CronTriggerSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,122 @@ +package org.quartz.core.jmx; + +import static javax.management.openmbean.SimpleType.STRING; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.quartz.CronTrigger; +import org.quartz.impl.triggers.CronTriggerImpl; +import org.quartz.spi.OperableTrigger; + +public class CronTriggerSupport { + private static final String COMPOSITE_TYPE_NAME = "CronTrigger"; + private static final String COMPOSITE_TYPE_DESCRIPTION = "CronTrigger Details"; + private static final String[] ITEM_NAMES = new String[] { "expression", "timeZone" }; + private static final String[] ITEM_DESCRIPTIONS = new String[] { "expression", "timeZone" }; + private static final OpenType[] ITEM_TYPES = new OpenType[] { STRING, STRING }; + private static final CompositeType COMPOSITE_TYPE; + private static final String TABULAR_TYPE_NAME = "CronTrigger collection"; + private static final String TABULAR_TYPE_DESCRIPTION = "CronTrigger collection"; + private static final TabularType TABULAR_TYPE; + + static { + try { + COMPOSITE_TYPE = new CompositeType(COMPOSITE_TYPE_NAME, + COMPOSITE_TYPE_DESCRIPTION, getItemNames(), getItemDescriptions(), + getItemTypes()); + TABULAR_TYPE = new TabularType(TABULAR_TYPE_NAME, + TABULAR_TYPE_DESCRIPTION, COMPOSITE_TYPE, getItemNames()); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + public static String[] getItemNames() { + List l = new ArrayList(Arrays.asList(ITEM_NAMES)); + l.addAll(Arrays.asList(TriggerSupport.getItemNames())); + return l.toArray(new String[l.size()]); + } + + public static String[] getItemDescriptions() { + List l = new ArrayList(Arrays.asList(ITEM_DESCRIPTIONS)); + l.addAll(Arrays.asList(TriggerSupport.getItemDescriptions())); + return l.toArray(new String[l.size()]); + } + + public static OpenType[] getItemTypes() { + List l = new ArrayList(Arrays.asList(ITEM_TYPES)); + l.addAll(Arrays.asList(TriggerSupport.getItemTypes())); + return l.toArray(new OpenType[l.size()]); + } + + public static CompositeData toCompositeData(CronTrigger trigger) { + try { + return new CompositeDataSupport(COMPOSITE_TYPE, ITEM_NAMES, + new Object[] { + trigger.getCronExpression(), + trigger.getTimeZone(), + trigger.getKey().getName(), + trigger.getKey().getGroup(), + trigger.getJobKey().getName(), + trigger.getJobKey().getGroup(), + trigger.getDescription(), + JobDataMapSupport.toTabularData(trigger + .getJobDataMap()), + trigger.getCalendarName(), + ((OperableTrigger)trigger).getFireInstanceId(), + trigger.getMisfireInstruction(), + trigger.getPriority(), trigger.getStartTime(), + trigger.getEndTime(), trigger.getNextFireTime(), + trigger.getPreviousFireTime(), + trigger.getFinalFireTime() }); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + public static TabularData toTabularData(List triggers) { + TabularData tData = new TabularDataSupport(TABULAR_TYPE); + if (triggers != null) { + ArrayList list = new ArrayList(); + for (CronTrigger trigger : triggers) { + list.add(toCompositeData(trigger)); + } + tData.putAll(list.toArray(new CompositeData[list.size()])); + } + return tData; + } + + public static OperableTrigger newTrigger(CompositeData cData) throws ParseException { + CronTriggerImpl result = new CronTriggerImpl(); + result.setCronExpression((String) cData.get("cronExpression")); + if(cData.containsKey("timeZone")) { + result.setTimeZone(TimeZone.getTimeZone((String)cData.get("timeZone"))); + } + TriggerSupport.initializeTrigger(result, cData); + return result; + } + + public static OperableTrigger newTrigger(Map attrMap) throws ParseException { + CronTriggerImpl result = new CronTriggerImpl(); + result.setCronExpression((String) attrMap.get("cronExpression")); + if(attrMap.containsKey("timeZone")) { + result.setTimeZone(TimeZone.getTimeZone((String)attrMap.get("timeZone"))); + } + TriggerSupport.initializeTrigger(result, attrMap); + return result; + } +} Index: 3rdParty_sources/quartz/org/quartz/core/jmx/JobDataMapSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/jmx/JobDataMapSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/jmx/JobDataMapSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,92 @@ +package org.quartz.core.jmx; + +import static javax.management.openmbean.SimpleType.STRING; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.quartz.JobDataMap; + +public class JobDataMapSupport { + private static final String typeName = "JobDataMap"; + private static final String[] keyValue = new String[] { "key", "value" }; + private static final OpenType[] openTypes = new OpenType[] { STRING, STRING }; + private static final CompositeType rowType; + public static final TabularType TABULAR_TYPE; + + static { + try { + rowType = new CompositeType(typeName, typeName, keyValue, keyValue, + openTypes); + TABULAR_TYPE = new TabularType(typeName, typeName, rowType, + new String[] { "key" }); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + public static JobDataMap newJobDataMap(TabularData tabularData) { + JobDataMap jobDataMap = new JobDataMap(); + + if(tabularData != null) { + for (final Iterator pos = tabularData.values().iterator(); pos.hasNext();) { + CompositeData cData = (CompositeData) pos.next(); + jobDataMap.put((String) cData.get("key"), (String) cData.get("value")); + } + } + + return jobDataMap; + } + + public static JobDataMap newJobDataMap(Map map) { + JobDataMap jobDataMap = new JobDataMap(); + + if(map != null) { + for (final Iterator pos = map.keySet().iterator(); pos.hasNext();) { + String key = pos.next(); + jobDataMap.put(key, map.get(key)); + } + } + + return jobDataMap; + } + + /** + * @return composite data + */ + public static CompositeData toCompositeData(String key, String value) { + try { + return new CompositeDataSupport(rowType, keyValue, new Object[] { + key, value }); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + /** + * @param jobDataMap + * @return TabularData + */ + public static TabularData toTabularData(JobDataMap jobDataMap) { + TabularData tData = new TabularDataSupport(TABULAR_TYPE); + ArrayList list = new ArrayList(); + Iterator iter = jobDataMap.keySet().iterator(); + while (iter.hasNext()) { + String key = iter.next(); + list.add(toCompositeData(key, String.valueOf(jobDataMap.get(key)))); + } + tData.putAll(list.toArray(new CompositeData[list.size()])); + return tData; + } + +} Index: 3rdParty_sources/quartz/org/quartz/core/jmx/JobDetailSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/jmx/JobDetailSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/jmx/JobDetailSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,142 @@ +package org.quartz.core.jmx; + +import static javax.management.openmbean.SimpleType.BOOLEAN; +import static javax.management.openmbean.SimpleType.STRING; + +import java.util.ArrayList; +import java.util.Map; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.quartz.Job; +import org.quartz.JobDetail; +import org.quartz.impl.JobDetailImpl; + +public class JobDetailSupport { + private static final String COMPOSITE_TYPE_NAME = "JobDetail"; + private static final String COMPOSITE_TYPE_DESCRIPTION = "Job Execution Details"; + private static final String[] ITEM_NAMES = new String[] { "name", "group", + "description", "jobClass", "jobDataMap", "durability", "shouldRecover",}; + private static final String[] ITEM_DESCRIPTIONS = new String[] { "name", + "group", "description", "jobClass", "jobDataMap", "durability", "shouldRecover",}; + private static final OpenType[] ITEM_TYPES = new OpenType[] { STRING, + STRING, STRING, STRING, JobDataMapSupport.TABULAR_TYPE, BOOLEAN, + BOOLEAN, }; + private static final CompositeType COMPOSITE_TYPE; + private static final String TABULAR_TYPE_NAME = "JobDetail collection"; + private static final String TABULAR_TYPE_DESCRIPTION = "JobDetail collection"; + private static final String[] INDEX_NAMES = new String[] { "name", "group" }; + private static final TabularType TABULAR_TYPE; + + static { + try { + COMPOSITE_TYPE = new CompositeType(COMPOSITE_TYPE_NAME, + COMPOSITE_TYPE_DESCRIPTION, ITEM_NAMES, ITEM_DESCRIPTIONS, + ITEM_TYPES); + TABULAR_TYPE = new TabularType(TABULAR_TYPE_NAME, + TABULAR_TYPE_DESCRIPTION, COMPOSITE_TYPE, INDEX_NAMES); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + /** + * @param cData + * @return JobDetail + */ + public static JobDetail newJobDetail(CompositeData cData) + throws ClassNotFoundException + { + JobDetailImpl jobDetail = new JobDetailImpl(); + + int i = 0; + jobDetail.setName((String) cData.get(ITEM_NAMES[i++])); + jobDetail.setGroup((String) cData.get(ITEM_NAMES[i++])); + jobDetail.setDescription((String) cData.get(ITEM_NAMES[i++])); + Class jobClass = Class.forName((String) cData.get(ITEM_NAMES[i++])); + @SuppressWarnings("unchecked") + Class jobClassTyped = (Class)jobClass; + jobDetail.setJobClass(jobClassTyped); + jobDetail.setJobDataMap(JobDataMapSupport.newJobDataMap((TabularData) cData.get(ITEM_NAMES[i++]))); + jobDetail.setDurability((Boolean) cData.get(ITEM_NAMES[i++])); + jobDetail.setRequestsRecovery((Boolean) cData.get(ITEM_NAMES[i++])); + + return jobDetail; + } + + /** + * @param attrMap the attributes that define the job + * @return JobDetail + */ + public static JobDetail newJobDetail(Map attrMap) + throws ClassNotFoundException + { + JobDetailImpl jobDetail = new JobDetailImpl(); + + int i = 0; + jobDetail.setName((String) attrMap.get(ITEM_NAMES[i++])); + jobDetail.setGroup((String) attrMap.get(ITEM_NAMES[i++])); + jobDetail.setDescription((String) attrMap.get(ITEM_NAMES[i++])); + Class jobClass = Class.forName((String) attrMap.get(ITEM_NAMES[i++])); + @SuppressWarnings("unchecked") + Class jobClassTyped = (Class)jobClass; + jobDetail.setJobClass(jobClassTyped); + if(attrMap.containsKey(ITEM_NAMES[i])) { + @SuppressWarnings("unchecked") + Map map = (Map)attrMap.get(ITEM_NAMES[i]); + jobDetail.setJobDataMap(JobDataMapSupport.newJobDataMap(map)); + } + i++; + if(attrMap.containsKey(ITEM_NAMES[i])) { + jobDetail.setDurability((Boolean) attrMap.get(ITEM_NAMES[i])); + } + i++; + if(attrMap.containsKey(ITEM_NAMES[i])) { + jobDetail.setRequestsRecovery((Boolean) attrMap.get(ITEM_NAMES[i])); + } + i++; + + return jobDetail; + } + + /** + * @param jobDetail + * @return CompositeData + */ + public static CompositeData toCompositeData(JobDetail jobDetail) { + try { + return new CompositeDataSupport(COMPOSITE_TYPE, ITEM_NAMES, + new Object[] { + jobDetail.getKey().getName(), + jobDetail.getKey().getGroup(), + jobDetail.getDescription(), + jobDetail.getJobClass().getName(), + JobDataMapSupport.toTabularData(jobDetail + .getJobDataMap()), + jobDetail.isDurable(), + jobDetail.requestsRecovery(), }); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + public static TabularData toTabularData(JobDetail[] jobDetails) { + TabularData tData = new TabularDataSupport(TABULAR_TYPE); + if (jobDetails != null) { + ArrayList list = new ArrayList(); + for (JobDetail jobDetail : jobDetails) { + list.add(toCompositeData(jobDetail)); + } + tData.putAll(list.toArray(new CompositeData[list.size()])); + } + return tData; + } + +} Index: 3rdParty_sources/quartz/org/quartz/core/jmx/JobExecutionContextSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/jmx/JobExecutionContextSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/jmx/JobExecutionContextSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,100 @@ +package org.quartz.core.jmx; + +import static javax.management.openmbean.SimpleType.BOOLEAN; +import static javax.management.openmbean.SimpleType.DATE; +import static javax.management.openmbean.SimpleType.INTEGER; +import static javax.management.openmbean.SimpleType.LONG; +import static javax.management.openmbean.SimpleType.STRING; + +import java.util.ArrayList; +import java.util.List; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.quartz.JobExecutionContext; +import org.quartz.SchedulerException; + +public class JobExecutionContextSupport { + private static final String COMPOSITE_TYPE_NAME = "JobExecutionContext"; + private static final String COMPOSITE_TYPE_DESCRIPTION = "Job Execution Instance Details"; + private static final String[] ITEM_NAMES = new String[] { "schedulerName", + "triggerName", "triggerGroup", "jobName", "jobGroup", "jobDataMap", + "calendarName", "recovering", "refireCount", "fireTime", + "scheduledFireTime", "previousFireTime", "nextFireTime", + "jobRunTime", "fireInstanceId" }; + private static final String[] ITEM_DESCRIPTIONS = new String[] { + "schedulerName", "triggerName", "triggerGroup", "jobName", + "jobGroup", "jobDataMap", "calendarName", "recovering", + "refireCount", "fireTime", "scheduledFireTime", "previousFireTime", + "nextFireTime", "jobRunTime", "fireInstanceId" }; + private static final OpenType[] ITEM_TYPES = new OpenType[] { STRING, + STRING, STRING, STRING, STRING, JobDataMapSupport.TABULAR_TYPE, + STRING, BOOLEAN, INTEGER, DATE, DATE, DATE, DATE, LONG, STRING }; + private static final CompositeType COMPOSITE_TYPE; + private static final String TABULAR_TYPE_NAME = "JobExecutionContextArray"; + private static final String TABULAR_TYPE_DESCRIPTION = "Array of composite JobExecutionContext"; + private static final String[] INDEX_NAMES = new String[] { "schedulerName", + "triggerName", "triggerGroup", "jobName", "jobGroup", "fireTime" }; + private static final TabularType TABULAR_TYPE; + + static { + try { + COMPOSITE_TYPE = new CompositeType(COMPOSITE_TYPE_NAME, + COMPOSITE_TYPE_DESCRIPTION, ITEM_NAMES, ITEM_DESCRIPTIONS, + ITEM_TYPES); + TABULAR_TYPE = new TabularType(TABULAR_TYPE_NAME, + TABULAR_TYPE_DESCRIPTION, COMPOSITE_TYPE, INDEX_NAMES); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + /** + * @return composite data + */ + public static CompositeData toCompositeData(JobExecutionContext jec) + throws SchedulerException { + try { + return new CompositeDataSupport(COMPOSITE_TYPE, ITEM_NAMES, + new Object[] { + jec.getScheduler().getSchedulerName(), + jec.getTrigger().getKey().getName(), + jec.getTrigger().getKey().getGroup(), + jec.getJobDetail().getKey().getName(), + jec.getJobDetail().getKey().getGroup(), + JobDataMapSupport.toTabularData(jec + .getMergedJobDataMap()), + jec.getTrigger().getCalendarName(), + jec.isRecovering(), + jec.getRefireCount(), + jec.getFireTime(), jec.getScheduledFireTime(), + jec.getPreviousFireTime(), jec.getNextFireTime(), + jec.getJobRunTime(), + jec.getFireInstanceId() }); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + /** + * @return array of region statistics + */ + public static TabularData toTabularData( + final List executingJobs) + throws SchedulerException { + List list = new ArrayList(); + for (JobExecutionContext executingJob : executingJobs) { + list.add(toCompositeData(executingJob)); + } + TabularData td = new TabularDataSupport(TABULAR_TYPE); + td.putAll(list.toArray(new CompositeData[list.size()])); + return td; + } +} Index: 3rdParty_sources/quartz/org/quartz/core/jmx/QuartzSchedulerMBean.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/jmx/QuartzSchedulerMBean.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/jmx/QuartzSchedulerMBean.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,319 @@ +package org.quartz.core.jmx; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +public interface QuartzSchedulerMBean { + static final String SCHEDULER_STARTED = "schedulerStarted"; + static final String SCHEDULER_PAUSED = "schedulerPaused"; + static final String SCHEDULER_SHUTDOWN = "schedulerShutdown"; + static final String SCHEDULER_ERROR = "schedulerError"; + + static final String JOB_ADDED = "jobAdded"; + static final String JOB_DELETED = "jobDeleted"; + static final String JOB_SCHEDULED = "jobScheduled"; + static final String JOB_UNSCHEDULED = "jobUnscheduled"; + + static final String JOBS_PAUSED = "jobsPaused"; + static final String JOBS_RESUMED = "jobsResumed"; + + static final String JOB_EXECUTION_VETOED = "jobExecutionVetoed"; + static final String JOB_TO_BE_EXECUTED = "jobToBeExecuted"; + static final String JOB_WAS_EXECUTED = "jobWasExecuted"; + + static final String TRIGGER_FINALIZED = "triggerFinalized"; + + static final String TRIGGERS_PAUSED = "triggersPaused"; + static final String TRIGGERS_RESUMED = "triggersResumed"; + + static final String SCHEDULING_DATA_CLEARED = "schedulingDataCleared"; + + static final String SAMPLED_STATISTICS_ENABLED = "sampledStatisticsEnabled"; + static final String SAMPLED_STATISTICS_RESET = "sampledStatisticsReset"; + + String getSchedulerName(); + + String getSchedulerInstanceId(); + + boolean isStandbyMode(); + + boolean isShutdown(); + + String getVersion(); + + String getJobStoreClassName(); + + String getThreadPoolClassName(); + + int getThreadPoolSize(); + + long getJobsScheduledMostRecentSample(); + + long getJobsExecutedMostRecentSample(); + + long getJobsCompletedMostRecentSample(); + + Map getPerformanceMetrics(); + + /** + * @return TabularData of CompositeData:JobExecutionContext + * @throws Exception + */ + TabularData getCurrentlyExecutingJobs() throws Exception; + + /** + * @return TabularData of CompositeData:JobDetail + * @throws Exception + * @see JobDetailSupport + */ + TabularData getAllJobDetails() throws Exception; + + /** + * @return List of CompositeData:[CronTrigger|SimpleTrigger] + * @throws Exception + * @see TriggerSupport + */ + List getAllTriggers() throws Exception; + + List getJobGroupNames() throws Exception; + + List getJobNames(String groupName) + throws Exception; + + /** + * @return CompositeData:JobDetail + * @throws Exception + * @see JobDetailSupport + */ + CompositeData getJobDetail(String jobName, String jobGroupName) throws Exception; + + boolean isStarted(); + + void start() throws Exception; + + void shutdown(); + + void standby(); + + void clear() throws Exception; + + /** + * Schedule an existing job with an existing trigger. + * + * @param jobName + * @param jobGroup + * @param triggerName + * @param triggerGroup + * @return date of nextFireTime + * @throws Exception + */ + Date scheduleJob(String jobName, String jobGroup, + String triggerName, String triggerGroup) throws Exception; + + /** + * Schedules a job using the given Cron/Simple triggerInfo. + * + * The triggerInfo and jobDetailInfo must contain well-known attribute values. + * TriggerInfo attributes: name, group, description, calendarName, priority, + * CronExpression | (startTime, endTime, repeatCount, repeatInterval) + * JobDetailInfo attributes: name, group, description, jobClass, jobDataMap, durability, + * shouldRecover + */ + void scheduleBasicJob(Map jobDetailInfo, Map triggerInfo) + throws Exception; + + /** + * Schedules an arbitrary job described by abstractJobInfo using a trigger specified by abstractTriggerInfo. + * + * AbtractTriggerInfo and AbstractJobInfo must contain the following String attributes. + * AbstractTriggerInfo: triggerClass, the fully-qualified class name of a concrete Trigger type + * AbstractJobInfo: jobDetailClass, the fully-qualified class name of a concrete JobDetail type + * + * If the Trigger and JobDetail can be successfully instantiated, the remaining attributes will be + * reflectively applied to those instances. The remaining attributes are limited to the types: + * Integer, Double, Float, String, Boolean, Date, Character, Map. + * Maps are further limited to containing values from the same set of types, less Map itself. + * + * @throws Exception + */ + void scheduleJob(Map abstractJobInfo, + Map abstractTriggerInfo) throws Exception; + + /** + * Schedules the specified job using a trigger described by abstractTriggerInfo, which must contain the + * fully-qualified trigger class name under the key "triggerClass." That trigger type must contain a + * no-arg constructor and have public access. Other attributes are applied reflectively and are limited + * to the types: + * Integer, Double, Float, String, Boolean, Date, Character, Map. + * Maps are limited to containing values from the same set of types, less Map itself. + * + * @param jobName + * @param jobGroup + * @param abstractTriggerInfo + * @throws Exception + */ + void scheduleJob(String jobName, String jobGroup, + Map abstractTriggerInfo) throws Exception; + + boolean unscheduleJob(String triggerName, String triggerGroup) throws Exception; + + boolean interruptJob(String jobName, String jobGroupName) throws Exception; + + boolean interruptJob(String fireInstanceId) throws Exception; + + void triggerJob(String jobName, String jobGroupName, + Map jobDataMap) throws Exception; + + boolean deleteJob(String jobName, String jobGroupName) + throws Exception; + + void addJob(CompositeData jobDetail, boolean replace) throws Exception; + + /** + * Adds a durable job described by abstractJobInfo, which must contain the fully-qualified JobDetail + * class name under the key "jobDetailClass." That JobDetail type must contain a no-arg constructor + * and have public access. Other attributes are applied reflectively and are limited + * to the types: + * Integer, Double, Float, String, Boolean, Date, Character, Map. + * Maps are limited to containing values from the same set of types, less Map itself. + * + * @param abstractJobInfo map of attributes defining job + * @param replace whether or not to replace a pre-existing job with the same key + * @throws Exception + */ + void addJob(Map abstractJobInfo, boolean replace) + throws Exception; + + void pauseJobGroup(String jobGroup) throws Exception; + + /** + * Pause all jobs whose group starts with jobGroupPrefix + * @throws Exception + */ + void pauseJobsStartingWith(String jobGroupPrefix) throws Exception; + + /** + * Pause all jobs whose group ends with jobGroupSuffix + */ + void pauseJobsEndingWith(String jobGroupSuffix) throws Exception; + + /** + * Pause all jobs whose group contains jobGroupToken + */ + void pauseJobsContaining(String jobGroupToken) throws Exception; + + /** + * Pause all jobs whose group is anything + */ + void pauseJobsAll() throws Exception; + + /** + * Resume all jobs in the given group + */ + void resumeJobGroup(String jobGroup) throws Exception; + + /** + * Resume all jobs whose group starts with jobGroupPrefix + */ + void resumeJobsStartingWith(String jobGroupPrefix) throws Exception; + + /** + * Resume all jobs whose group ends with jobGroupSuffix + */ + void resumeJobsEndingWith(String jobGroupSuffix) throws Exception; + + /** + * Resume all jobs whose group contains jobGroupToken + */ + void resumeJobsContaining(String jobGroupToken) throws Exception; + + /** + * Resume all jobs whose group is anything + */ + void resumeJobsAll() throws Exception; + + void pauseJob(String jobName, String groupName) throws Exception; + + void resumeJob(String jobName, String jobGroupName) throws Exception; + + List getTriggerGroupNames() throws Exception; + + List getTriggerNames(String triggerGroupName) throws Exception; + + CompositeData getTrigger(String triggerName, String triggerGroupName) throws Exception; + + String getTriggerState(String triggerName, String triggerGroupName) throws Exception; + + /** + * @return List of CompositeData:[CronTrigger|SimpleTrigger] for the specified job. + * @see TriggerSupport + */ + List getTriggersOfJob(String jobName, String jobGroupName) throws Exception; + + Set getPausedTriggerGroups() throws Exception; + + void pauseAllTriggers() throws Exception; + + void resumeAllTriggers() throws Exception; + + void pauseTriggerGroup(String triggerGroup) throws Exception; + + /** + * Pause all triggers whose group starts with triggerGroupPrefix + */ + void pauseTriggersStartingWith(String triggerGroupPrefix) throws Exception; + + /** + * Pause all triggers whose group ends with triggerGroupSuffix + */ + void pauseTriggersEndingWith(String suffix) throws Exception; + + /** + * Pause all triggers whose group contains triggerGroupToken + */ + void pauseTriggersContaining(String triggerGroupToken) throws Exception; + + /** + * Pause all triggers whose group is anything + */ + void pauseTriggersAll() throws Exception; + + void resumeTriggerGroup(String triggerGroup) throws Exception; + + /** + * Resume all triggers whose group starts with triggerGroupPrefix + */ + void resumeTriggersStartingWith(String triggerGroupPrefix) throws Exception; + + /** + * Resume all triggers whose group ends with triggerGroupSuffix + */ + void resumeTriggersEndingWith(String triggerGroupSuffix) throws Exception; + + /** + * Resume all triggers whose group contains triggerGroupToken + */ + void resumeTriggersContaining(String triggerGroupToken) throws Exception; + + /** + * Resume all triggers whose group is anything + */ + void resumeTriggersAll() throws Exception; + + void pauseTrigger(String triggerName, String triggerGroupName) throws Exception; + + void resumeTrigger(String triggerName, String triggerGroupName) throws Exception; + + List getCalendarNames() throws Exception; + + void deleteCalendar(String name) throws Exception; + + void setSampledStatisticsEnabled(boolean enabled); + + boolean isSampledStatisticsEnabled(); +} Index: 3rdParty_sources/quartz/org/quartz/core/jmx/SimpleTriggerSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/jmx/SimpleTriggerSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/jmx/SimpleTriggerSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,127 @@ +package org.quartz.core.jmx; + +import static javax.management.openmbean.SimpleType.INTEGER; +import static javax.management.openmbean.SimpleType.LONG; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.quartz.SimpleTrigger; +import org.quartz.impl.triggers.SimpleTriggerImpl; +import org.quartz.spi.OperableTrigger; + +public class SimpleTriggerSupport { + private static final String COMPOSITE_TYPE_NAME = "SimpleTrigger"; + private static final String COMPOSITE_TYPE_DESCRIPTION = "SimpleTrigger Details"; + private static final String[] ITEM_NAMES = new String[] { "repeatCount", "repeatInterval", "timesTriggered" }; + private static final String[] ITEM_DESCRIPTIONS = new String[] { "repeatCount", "repeatInterval", "timesTriggered" }; + private static final OpenType[] ITEM_TYPES = new OpenType[] { INTEGER, LONG, INTEGER }; + private static final CompositeType COMPOSITE_TYPE; + private static final String TABULAR_TYPE_NAME = "SimpleTrigger collection"; + private static final String TABULAR_TYPE_DESCRIPTION = "SimpleTrigger collection"; + private static final TabularType TABULAR_TYPE; + + static { + try { + COMPOSITE_TYPE = new CompositeType(COMPOSITE_TYPE_NAME, + COMPOSITE_TYPE_DESCRIPTION, getItemNames(), getItemDescriptions(), + getItemTypes()); + TABULAR_TYPE = new TabularType(TABULAR_TYPE_NAME, + TABULAR_TYPE_DESCRIPTION, COMPOSITE_TYPE, getItemNames()); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + public static String[] getItemNames() { + List l = new ArrayList(Arrays.asList(ITEM_NAMES)); + l.addAll(Arrays.asList(TriggerSupport.getItemNames())); + return l.toArray(new String[l.size()]); + } + + public static String[] getItemDescriptions() { + List l = new ArrayList(Arrays.asList(ITEM_DESCRIPTIONS)); + l.addAll(Arrays.asList(TriggerSupport.getItemDescriptions())); + return l.toArray(new String[l.size()]); + } + + public static OpenType[] getItemTypes() { + List l = new ArrayList(Arrays.asList(ITEM_TYPES)); + l.addAll(Arrays.asList(TriggerSupport.getItemTypes())); + return l.toArray(new OpenType[l.size()]); + } + + public static CompositeData toCompositeData(SimpleTrigger trigger) { + try { + return new CompositeDataSupport(COMPOSITE_TYPE, ITEM_NAMES, + new Object[] { + trigger.getRepeatCount(), + trigger.getRepeatInterval(), + trigger.getTimesTriggered(), + trigger.getKey().getName(), + trigger.getKey().getGroup(), + trigger.getJobKey().getName(), + trigger.getJobKey().getGroup(), + trigger.getDescription(), + JobDataMapSupport.toTabularData(trigger + .getJobDataMap()), + trigger.getCalendarName(), + ((OperableTrigger)trigger).getFireInstanceId(), + trigger.getMisfireInstruction(), + trigger.getPriority(), trigger.getStartTime(), + trigger.getEndTime(), trigger.getNextFireTime(), + trigger.getPreviousFireTime(), + trigger.getFinalFireTime() }); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + public static TabularData toTabularData(List triggers) { + TabularData tData = new TabularDataSupport(TABULAR_TYPE); + if (triggers != null) { + ArrayList list = new ArrayList(); + for (SimpleTrigger trigger : triggers) { + list.add(toCompositeData(trigger)); + } + tData.putAll(list.toArray(new CompositeData[list.size()])); + } + return tData; + } + + public static OperableTrigger newTrigger(CompositeData cData) throws ParseException { + SimpleTriggerImpl result = new SimpleTriggerImpl(); + result.setRepeatCount(((Integer) cData.get("repeatCount")).intValue()); + result.setRepeatInterval(((Long) cData.get("repeatInterval")).longValue()); + result.setTimesTriggered(((Integer) cData.get("timesTriggered")).intValue()); + TriggerSupport.initializeTrigger(result, cData); + return result; + } + + public static OperableTrigger newTrigger(Map attrMap) throws ParseException { + SimpleTriggerImpl result = new SimpleTriggerImpl(); + if(attrMap.containsKey("repeatCount")) { + result.setRepeatCount(((Integer) attrMap.get("repeatCount")).intValue()); + } + if(attrMap.containsKey("repeatInterval")) { + result.setRepeatInterval(((Long) attrMap.get("repeatInterval")).longValue()); + } + if(attrMap.containsKey("timesTriggered")) { + result.setTimesTriggered(((Integer) attrMap.get("timesTriggered")).intValue()); + } + TriggerSupport.initializeTrigger(result, attrMap); + return result; + } +} Index: 3rdParty_sources/quartz/org/quartz/core/jmx/TriggerSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/jmx/TriggerSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/jmx/TriggerSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,195 @@ +package org.quartz.core.jmx; + +import static javax.management.openmbean.SimpleType.DATE; +import static javax.management.openmbean.SimpleType.INTEGER; +import static javax.management.openmbean.SimpleType.STRING; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeDataSupport; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.OpenType; +import javax.management.openmbean.TabularData; +import javax.management.openmbean.TabularDataSupport; +import javax.management.openmbean.TabularType; + +import org.quartz.JobKey; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.quartz.spi.MutableTrigger; +import org.quartz.spi.OperableTrigger; + +public class TriggerSupport { + private static final String COMPOSITE_TYPE_NAME = "Trigger"; + private static final String COMPOSITE_TYPE_DESCRIPTION = "Trigger Details"; + private static final String[] ITEM_NAMES = new String[] { "name", + "group", "jobName", "jobGroup", "description", "jobDataMap", + "calendarName", "fireInstanceId", "misfireInstruction", "priority", + "startTime", "endTime", "nextFireTime", "previousFireTime", "finalFireTime" }; + private static final String[] ITEM_DESCRIPTIONS = new String[] { "name", + "group", "jobName", "jobGroup", "description", "jobDataMap", + "calendarName", "fireInstanceId", "misfireInstruction", "priority", + "startTime", "endTime", "nextFireTime", "previousFireTime", "finalFireTime" }; + private static final OpenType[] ITEM_TYPES = new OpenType[] { STRING, + STRING, STRING, STRING, STRING, JobDataMapSupport.TABULAR_TYPE, + STRING, STRING, INTEGER, INTEGER, + DATE, DATE, DATE, DATE, DATE }; + private static final CompositeType COMPOSITE_TYPE; + private static final String TABULAR_TYPE_NAME = "Trigger collection"; + private static final String TABULAR_TYPE_DESCRIPTION = "Trigger collection"; + private static final String[] INDEX_NAMES = new String[] { "name", "group" }; + private static final TabularType TABULAR_TYPE; + + static { + try { + COMPOSITE_TYPE = new CompositeType(COMPOSITE_TYPE_NAME, + COMPOSITE_TYPE_DESCRIPTION, ITEM_NAMES, ITEM_DESCRIPTIONS, + ITEM_TYPES); + TABULAR_TYPE = new TabularType(TABULAR_TYPE_NAME, + TABULAR_TYPE_DESCRIPTION, COMPOSITE_TYPE, INDEX_NAMES); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + public static String[] getItemNames() { + return ITEM_NAMES; + } + + public static String[] getItemDescriptions() { + return ITEM_DESCRIPTIONS; + } + + public static OpenType[] getItemTypes() { + return ITEM_TYPES; + } + + public String[] getIndexNames() { + return INDEX_NAMES; + } + + public static CompositeData toCompositeData(Trigger trigger) { + try { + return new CompositeDataSupport(COMPOSITE_TYPE, ITEM_NAMES, + new Object[] { + trigger.getKey().getName(), + trigger.getKey().getGroup(), + trigger.getJobKey().getName(), + trigger.getJobKey().getGroup(), + trigger.getDescription(), + JobDataMapSupport.toTabularData(trigger + .getJobDataMap()), + trigger.getCalendarName(), + ((OperableTrigger)trigger).getFireInstanceId(), + trigger.getMisfireInstruction(), + trigger.getPriority(), trigger.getStartTime(), + trigger.getEndTime(), trigger.getNextFireTime(), + trigger.getPreviousFireTime(), + trigger.getFinalFireTime() }); + } catch (OpenDataException e) { + throw new RuntimeException(e); + } + } + + public static TabularData toTabularData(List triggers) { + TabularData tData = new TabularDataSupport(TABULAR_TYPE); + if (triggers != null) { + ArrayList list = new ArrayList(); + for (Trigger trigger : triggers) { + list.add(toCompositeData(trigger)); + } + tData.putAll(list.toArray(new CompositeData[list.size()])); + } + return tData; + } + + public static List toCompositeList(List triggers) { + List result = new ArrayList(); + for(Trigger trigger : triggers) { + CompositeData cData = TriggerSupport.toCompositeData(trigger); + if(cData != null) { + result.add(cData); + } + } + return result; + } + + public static void initializeTrigger(MutableTrigger trigger, CompositeData cData) { + trigger.setDescription((String) cData.get("description")); + trigger.setCalendarName((String) cData.get("calendarName")); + if(cData.containsKey("priority")) { + trigger.setPriority(((Integer)cData.get("priority")).intValue()); + } + if(cData.containsKey("jobDataMap")) { + trigger.setJobDataMap(JobDataMapSupport.newJobDataMap((TabularData)cData.get("jobDataMap"))); + } + Date startTime; + if(cData.containsKey("startTime")) { + startTime = (Date) cData.get("startTime"); + } else { + startTime = new Date(); + } + trigger.setStartTime(startTime); + trigger.setEndTime((Date) cData.get("endTime")); + if(cData.containsKey("misfireInstruction")) { + trigger.setMisfireInstruction(((Integer)cData.get("misfireInstruction")).intValue()); + } + trigger.setKey(new TriggerKey((String) cData.get("name"), (String) cData.get("group"))); + trigger.setJobKey(new JobKey((String) cData.get("jobName"), (String) cData.get("jobGroup"))); + } + + public static void initializeTrigger(MutableTrigger trigger, Map attrMap) { + trigger.setDescription((String) attrMap.get("description")); + trigger.setCalendarName((String) attrMap.get("calendarName")); + if(attrMap.containsKey("priority")) { + trigger.setPriority(((Integer)attrMap.get("priority")).intValue()); + } + if(attrMap.containsKey("jobDataMap")) { + @SuppressWarnings("unchecked") // cast as expected. + Map mapTyped = (Map)attrMap.get("jobDataMap"); + trigger.setJobDataMap(JobDataMapSupport.newJobDataMap(mapTyped)); + } + Date startTime; + if(attrMap.containsKey("startTime")) { + startTime = (Date) attrMap.get("startTime"); + } else { + startTime = new Date(); + } + trigger.setStartTime(startTime); + if(attrMap.containsKey("endTime")) { + trigger.setEndTime((Date) attrMap.get("endTime")); + } + if(attrMap.containsKey("misfireInstruction")) { + trigger.setMisfireInstruction(((Integer)attrMap.get("misfireInstruction")).intValue()); + } + trigger.setKey(new TriggerKey((String) attrMap.get("name"), (String) attrMap.get("group"))); + trigger.setJobKey(new JobKey((String) attrMap.get("jobName"), (String) attrMap.get("jobGroup"))); + } + + public static OperableTrigger newTrigger(CompositeData cData) throws ParseException { + OperableTrigger result = null; + if(cData.containsKey("cronExpression")) { + result = CronTriggerSupport.newTrigger(cData); + } else { + result = SimpleTriggerSupport.newTrigger(cData); + } + return result; + } + + public static OperableTrigger newTrigger(Map attrMap) throws ParseException { + OperableTrigger result = null; + if(attrMap.containsKey("cronExpression")) { + result = CronTriggerSupport.newTrigger(attrMap); + } else { + result = SimpleTriggerSupport.newTrigger(attrMap); + } + return result; + } + +} Index: 3rdParty_sources/quartz/org/quartz/core/mbeans-descriptors.xml =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/mbeans-descriptors.xml (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/mbeans-descriptors.xml (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: 3rdParty_sources/quartz/org/quartz/core/package.html =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/core/package.html (.../package.html) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/core/package.html (.../package.html) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -8,8 +8,8 @@


-See the Quartz project - at Open Symphony for more information. +See the Quartz project + for more information. \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/core/quartz-build.properties =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/core/quartz-build.properties (revision 0) +++ 3rdParty_sources/quartz/org/quartz/core/quartz-build.properties (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,4 @@ +fullname=${name} +name=${artifactId} +version=${version} + Index: 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/JBoss4RMIRemoteMBeanScheduler.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/JBoss4RMIRemoteMBeanScheduler.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/JBoss4RMIRemoteMBeanScheduler.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,132 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.ee.jmx.jboss; + +import org.quartz.SchedulerException; +import org.quartz.impl.RemoteMBeanScheduler; + +import javax.management.AttributeList; +import javax.management.MBeanServerConnection; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import java.util.Arrays; +import java.util.Properties; + +/** + *

+ * An implementation of the Scheduler interface that remotely + * proxies all method calls to the equivalent call on a given QuartzScheduler + * instance, via JBoss's JMX RMIAdaptor. + *

+ * + *

+ * Set the providerURL property to your MBean server URL. + * This defaults to: jnp://localhost:1099 + *

+ * + * @see org.quartz.Scheduler + * @see org.quartz.core.QuartzScheduler + */ +public class JBoss4RMIRemoteMBeanScheduler extends RemoteMBeanScheduler { + + private static final String DEFAULT_PROVIDER_URL = "jnp://localhost:1099"; + private static final String RMI_ADAPTOR_JNDI_NAME = "jmx/rmi/RMIAdaptor"; + + private MBeanServerConnection server = null; + private String providerURL = DEFAULT_PROVIDER_URL; + + public JBoss4RMIRemoteMBeanScheduler() throws SchedulerException { + } + + + /** + * Set the remote MBean server URL. + * + * Defaults to: jnp://localhost:1099 + */ + public void setProviderURL(String providerURL) { + this.providerURL = providerURL; + } + + /** + * Initialize this remote MBean scheduler, getting the JBoss + * RMIAdaptor for communication. + */ + @Override + public void initialize() throws SchedulerException { + InitialContext ctx = null; + try { + ctx = new InitialContext(getContextProperties()); + server = (MBeanServerConnection)ctx.lookup(RMI_ADAPTOR_JNDI_NAME); + } catch (Exception e) { + throw new SchedulerException("Failed to lookup JBoss JMX RMI Adaptor.", e); + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (NamingException ignore) { + } + } + } + } + + /** + * Get the properties to use when creating a JNDI InitialContext. + * + *

+ * This method is broken out so it can be extended to pass credentials + * or other properties not currently supported. + *

+ */ + protected Properties getContextProperties() { + Properties props = new Properties(); + props.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); + props.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces"); + props.put(Context.PROVIDER_URL, providerURL); + + return props; + } + + @Override + protected Object getAttribute(String attribute) throws SchedulerException { + try { + return server.getAttribute(getSchedulerObjectName(), attribute); + } catch (Exception e) { + throw new SchedulerException("Failed to get Scheduler MBean attribute: " + attribute, e); + } + } + + @Override + protected AttributeList getAttributes(String[] attributes) throws SchedulerException { + try { + return server.getAttributes(getSchedulerObjectName(), attributes); + } catch (Exception e) { + throw new SchedulerException("Failed to get Scheduler MBean attributes: " + Arrays.asList(attributes), e); + } + } + + @Override + protected Object invoke(String operationName, Object[] params, + String[] signature) throws SchedulerException { + try { + return server.invoke(getSchedulerObjectName(), operationName, params, signature); + } catch (Exception e) { + throw new SchedulerException( + "Failed to invoke Scheduler MBean operation: " + operationName, e); + } + } +} Index: 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/QuartzService.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/QuartzService.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/QuartzService.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,336 @@ + +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.ee.jmx.jboss; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +import javax.naming.InitialContext; +import javax.naming.Name; +import javax.naming.NamingException; + +import org.quartz.Scheduler; +import org.quartz.SchedulerConfigException; +import org.quartz.SchedulerException; +import org.quartz.impl.StdSchedulerFactory; + +import org.jboss.naming.NonSerializableFactory; +import org.jboss.system.ServiceMBeanSupport; + +/** + * JBoss specific MBean implementation for configuring, starting, and + * binding to JNDI a Quartz Scheduler instance. + * + *

+ * Sample MBean deployment descriptor: + * quartz-service.xml + *

+ * + *

+ * Note: The Scheduler instance bound to JNDI is not Serializable, so + * you will get a null reference back if you try to retrieve it from outside + * the JBoss server in which it was bound. If you have a need for remote + * access to a Scheduler instance you may want to consider using Quartz's RMI + * support instead. + *

+ * + * @see org.quartz.ee.jmx.jboss.QuartzServiceMBean + * + * @author Andrew Collins + */ +public class QuartzService extends ServiceMBeanSupport implements + QuartzServiceMBean { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private Properties properties; + + private StdSchedulerFactory schedulerFactory; + + private String jndiName; + + private String propertiesFile; + + private boolean error; + + private boolean useProperties; + + private boolean usePropertiesFile; + + /* + * If true, the scheduler will be started. If false, the scheduler is initailized + * (and available) but start() is not called - it will not execute jobs. + */ + private boolean startScheduler = true; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public QuartzService() { + // flag initialization errors + error = false; + + // use PropertiesFile attribute + usePropertiesFile = false; + propertiesFile = ""; + + // use Properties attribute + useProperties = false; + properties = new Properties(); + + // default JNDI name for Scheduler + jndiName = "Quartz"; + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public void setJndiName(String jndiName) throws Exception { + String oldName = this.jndiName; + this.jndiName = jndiName; + + if (super.getState() == STARTED) { + unbind(oldName); + + try { + rebind(); + } catch (NamingException ne) { + log.error("Failed to rebind Scheduler", ne); + + throw new SchedulerConfigException( + "Failed to rebind Scheduler - ", ne); + } + } + } + + public String getJndiName() { + return jndiName; + } + + @Override + public String getName() { + return "QuartzService(" + jndiName + ")"; + } + + public void setProperties(String properties) { + if (usePropertiesFile) { + log + .error("Must specify only one of 'Properties' or 'PropertiesFile'"); + + error = true; + + return; + } + + useProperties = true; + + try { + properties = properties.replace(File.separator, "/"); + ByteArrayInputStream bais = new ByteArrayInputStream(properties + .getBytes()); + this.properties = new Properties(); + this.properties.load(bais); + } catch (IOException ioe) { + // should not happen + } + } + + public String getProperties() { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + properties.store(baos, ""); + + return new String(baos.toByteArray()); + } catch (IOException ioe) { + // should not happen + return ""; + } + } + + public void setPropertiesFile(String propertiesFile) { + if (useProperties) { + log + .error("Must specify only one of 'Properties' or 'PropertiesFile'"); + + error = true; + + return; + } + + usePropertiesFile = true; + + this.propertiesFile = propertiesFile; + } + + public String getPropertiesFile() { + return propertiesFile; + } + + public void setStartScheduler(boolean startScheduler) { + this.startScheduler = startScheduler; + } + + public boolean getStartScheduler() { + return startScheduler; + } + + @Override + public void createService() throws Exception { + log.info("Create QuartzService(" + jndiName + ")..."); + + if (error) { + log + .error("Must specify only one of 'Properties' or 'PropertiesFile'"); + + throw new Exception( + "Must specify only one of 'Properties' or 'PropertiesFile'"); + } + + schedulerFactory = new StdSchedulerFactory(); + + try { + if (useProperties) { + schedulerFactory.initialize(properties); + } + + if (usePropertiesFile) { + schedulerFactory.initialize(propertiesFile); + } + } catch (Exception e) { + log.error("Failed to initialize Scheduler", e); + + throw new SchedulerConfigException( + "Failed to initialize Scheduler - ", e); + } + + log.info("QuartzService(" + jndiName + ") created."); + } + + @Override + public void destroyService() throws Exception { + log.info("Destroy QuartzService(" + jndiName + ")..."); + + schedulerFactory = null; + + log.info("QuartzService(" + jndiName + ") destroyed."); + } + + @Override + public void startService() throws Exception { + log.info("Start QuartzService(" + jndiName + ")..."); + + try { + rebind(); + } catch (NamingException ne) { + log.error("Failed to rebind Scheduler", ne); + + throw new SchedulerConfigException("Failed to rebind Scheduler - ", + ne); + } + + try { + Scheduler scheduler = schedulerFactory.getScheduler(); + + if (startScheduler) { + scheduler.start(); + } else { + log.info("Skipping starting the scheduler (will not run jobs)."); + } + } catch (Exception e) { + log.error("Failed to start Scheduler", e); + + throw new SchedulerConfigException("Failed to start Scheduler - ", + e); + } + + log.info("QuartzService(" + jndiName + ") started."); + } + + @Override + public void stopService() throws Exception { + log.info("Stop QuartzService(" + jndiName + ")..."); + + try { + Scheduler scheduler = schedulerFactory.getScheduler(); + + scheduler.shutdown(); + } catch (Exception e) { + log.error("Failed to shutdown Scheduler", e); + + throw new SchedulerConfigException( + "Failed to shutdown Scheduler - ", e); + } + + unbind(jndiName); + + log.info("QuartzService(" + jndiName + ") stopped."); + } + + private void rebind() throws NamingException, SchedulerException { + InitialContext rootCtx = null; + try { + rootCtx = new InitialContext(); + Name fullName = rootCtx.getNameParser("").parse(jndiName); + Scheduler scheduler = schedulerFactory.getScheduler(); + NonSerializableFactory.rebind(fullName, scheduler, true); + } finally { + if (rootCtx != null) { + try { + rootCtx.close(); + } catch (NamingException ignore) {} + } + } + } + + private void unbind(String name) { + InitialContext rootCtx = null; + try { + rootCtx = new InitialContext(); + rootCtx.unbind(name); + NonSerializableFactory.unbind(name); + } catch (NamingException e) { + log.warn("Failed to unbind scheduler with jndiName: " + name, e); + } finally { + if (rootCtx != null) { + try { + rootCtx.close(); + } catch (NamingException ignore) {} + } + } + } +} Index: 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/QuartzServiceMBean.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/QuartzServiceMBean.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/QuartzServiceMBean.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,63 @@ + +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.ee.jmx.jboss; + +import org.jboss.system.ServiceMBean; + +/** + * Interface exposed via JMX for MBean for configuring, starting, and + * binding to JNDI a Quartz Scheduler instance. + * + *

+ * Sample MBean deployment descriptor: + * quartz-service.xml + *

+ * + *

+ * Note: The Scheduler instance bound to JNDI is not Serializable, so + * you will get a null reference back if you try to retrieve it from outside + * the JBoss server in which it was bound. If you have a need for remote + * access to a Scheduler instance you may want to consider using Quartz's RMI + * support instead. + *

+ * + * @see org.quartz.ee.jmx.jboss.QuartzService + * + * @author Andrew Collins + */ +public interface QuartzServiceMBean extends ServiceMBean { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + void setJndiName(String jndiName) throws Exception; + + String getJndiName(); + + void setProperties(String properties); + + void setPropertiesFile(String propertiesFile); + + void setStartScheduler(boolean startScheduler); +} Index: 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/doc-files/quartz-service.xml =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/doc-files/quartz-service.xml (revision 0) +++ 3rdParty_sources/quartz/org/quartz/ee/jmx/jboss/doc-files/quartz-service.xml (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,104 @@ + + + + + + + + + jboss.jca:service=DataSourceBinding,name=QuartzDS + + + + + + + + + + + + + + + + + + + # Default Properties file for use by StdSchedulerFactory + # to create a Quartz Scheduler Instance, if a different + # properties file is not explicitly specified. + # + + # org.quartz.scheduler.classLoadHelper.class = + + org.quartz.scheduler.instanceName = DefaultQuartzScheduler + org.quartz.scheduler.rmi.export = false + org.quartz.scheduler.rmi.proxy = false + org.quartz.scheduler.xaTransacted = false + + org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool + org.quartz.threadPool.threadCount = 5 + org.quartz.threadPool.threadPriority = 4 + + org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT + org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate + org.quartz.jobStore.dataSource = QUARTZ + org.quartz.jobStore.nonManagedTXDataSource = QUARTZ_NO_TX + org.quartz.jobStore.tablePrefix = QRTZ_ + org.quartz.dataSource.QUARTZ.jndiURL = java:/jdbc/QuartzDS + org.quartz.dataSource.QUARTZ_NO_TX.jndiURL = java:/jdbc/QuartzNoTxDS + + + + + + + + Index: 3rdParty_sources/quartz/org/quartz/ee/jta/JTAAnnotationAwareJobRunShellFactory.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/ee/jta/JTAAnnotationAwareJobRunShellFactory.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/ee/jta/JTAAnnotationAwareJobRunShellFactory.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,114 @@ + +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.ee.jta; + +import org.quartz.ExecuteInJTATransaction; +import org.quartz.Scheduler; +import org.quartz.SchedulerConfigException; +import org.quartz.SchedulerException; +import org.quartz.core.JobRunShell; +import org.quartz.core.JobRunShellFactory; +import org.quartz.spi.TriggerFiredBundle; +import org.quartz.utils.ClassUtils; + +/** + *

+ * Responsible for creating the instances of a {@link JobRunShell} + * to be used within the {@link org.quartz.core.QuartzScheduler} + * instance. It will create a standard {@link JobRunShell} + * unless the job class has the {@link ExecuteInJTATransaction} + * annotation in which case it will create a {@link JTAJobRunShell}. + *

+ * + *

+ * This implementation does not re-use any objects, it simply makes a new + * JTAJobRunShell each time borrowJobRunShell() is called. + *

+ * + * @author James House + */ +public class JTAAnnotationAwareJobRunShellFactory implements JobRunShellFactory { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private Scheduler scheduler; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public JTAAnnotationAwareJobRunShellFactory() { + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 sched) + throws SchedulerConfigException { + this.scheduler = sched; + } + + /** + *

+ * Called by the {@link org.quartz.core.QuartzSchedulerThread} + * to obtain instances of + * {@link org.quartz.core.JobRunShell}. + *

+ */ + public JobRunShell createJobRunShell(TriggerFiredBundle bundle) + throws SchedulerException { + ExecuteInJTATransaction jtaAnnotation = ClassUtils.getAnnotation(bundle.getJobDetail().getJobClass(), ExecuteInJTATransaction.class); + if(jtaAnnotation == null) + return new JobRunShell(scheduler, bundle); + else { + int timeout = jtaAnnotation.timeout(); + if (timeout >= 0) { + return new JTAJobRunShell(scheduler, bundle, timeout); + } else { + return new JTAJobRunShell(scheduler, bundle); + } + } + } + + +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/ee/jta/JTAJobRunShell.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/ee/jta/JTAJobRunShell.java (.../JTAJobRunShell.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/ee/jta/JTAJobRunShell.java (.../JTAJobRunShell.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,9 +16,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.ee.jta; import javax.transaction.Status; @@ -28,8 +25,7 @@ import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.core.JobRunShell; -import org.quartz.core.JobRunShellFactory; -import org.quartz.core.SchedulingContext; +import org.quartz.spi.TriggerFiredBundle; /** *

@@ -51,11 +47,10 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + private final Integer transactionTimeout; private UserTransaction ut; - private UserTransactionHelper userTxHelper; - /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -69,14 +64,21 @@ * Create a JTAJobRunShell instance with the given settings. *

*/ - public JTAJobRunShell(JobRunShellFactory jobRunShellFactory, - Scheduler scheduler, SchedulingContext schdCtxt, - UserTransactionHelper userTxHelper) { - super(jobRunShellFactory, scheduler, schdCtxt); - - this.userTxHelper = userTxHelper; + public JTAJobRunShell(Scheduler scheduler, TriggerFiredBundle bndle) { + super(scheduler, bndle); + this.transactionTimeout = null; } + /** + *

+ * Create a JTAJobRunShell instance with the given settings. + *

+ */ + public JTAJobRunShell(Scheduler scheduler, TriggerFiredBundle bndle, int timeout) { + super(scheduler, bndle); + this.transactionTimeout = timeout; + } + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -85,57 +87,93 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + @Override 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 { - log.debug("Looking up UserTransaction."); - ut = userTxHelper.lookup(); + getLog().debug("Looking up UserTransaction."); + ut = UserTransactionHelper.lookupUserTransaction(); + if (transactionTimeout != null) { + ut.setTransactionTimeout(transactionTimeout); + } - log.debug("Beginning UserTransaction."); + 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(); + } } } + @Override protected void complete(boolean successfulExecution) - throws SchedulerException { - - if (ut == null) return; - - try { - if (ut.getStatus() == Status.STATUS_MARKED_ROLLBACK) { - log.debug("UserTransaction marked for rollback only."); - successfulExecution = false; - } - } catch (SystemException e) { - throw new SchedulerException( - "JTAJobRunShell could not read UserTransaction status.", e); + throws SchedulerException { + if (ut == null) { + return; } - if (successfulExecution) { + try { try { - log.debug("Committing UserTransaction."); - ut.commit(); - } catch (Exception nse) { + 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 commit UserTransaction.", nse); + "JTAJobRunShell could not read UserTransaction status.", e); } - } else { - try { - log.debug("Rolling-back UserTransaction."); - ut.rollback(); - } catch (Exception nse) { - throw new SchedulerException( - "JTAJobRunShell could not rollback UserTransaction.", - nse); + + 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(); } - - ut = null; } + /** + * Override passivate() to ensure we always cleanup the UserTransaction. + */ + @Override + public void passivate() { + cleanupUserTransaction(); + super.passivate(); + } + + private void cleanupUserTransaction() { + if (ut != null) { + UserTransactionHelper.returnUserTransaction(ut); + ut = null; + } + } } \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/ee/jta/JTAJobRunShellFactory.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/ee/jta/JTAJobRunShellFactory.java (.../JTAJobRunShellFactory.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/ee/jta/JTAJobRunShellFactory.java (.../JTAJobRunShellFactory.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,16 +16,14 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.ee.jta; import org.quartz.Scheduler; import org.quartz.SchedulerConfigException; +import org.quartz.SchedulerException; import org.quartz.core.JobRunShell; import org.quartz.core.JobRunShellFactory; -import org.quartz.core.SchedulingContext; +import org.quartz.spi.TriggerFiredBundle; /** *

@@ -53,10 +51,6 @@ private Scheduler scheduler; - private SchedulingContext schedCtxt; - - private UserTransactionHelper userTxHelper; - /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -65,8 +59,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - public JTAJobRunShellFactory(UserTransactionHelper userTxHelper) { - this.userTxHelper = userTxHelper; + public JTAJobRunShellFactory() { } /* @@ -86,10 +79,9 @@ * operations with the JobStore. *

*/ - public void initialize(Scheduler scheduler, SchedulingContext schedCtxt) - throws SchedulerConfigException { - this.scheduler = scheduler; - this.schedCtxt = schedCtxt; + public void initialize(Scheduler sched) + throws SchedulerConfigException { + this.scheduler = sched; } /** @@ -99,19 +91,11 @@ * {@link org.quartz.core.JobRunShell}
. *

*/ - public JobRunShell borrowJobRunShell() { - return new JTAJobRunShell(this, scheduler, schedCtxt, userTxHelper); + public JobRunShell createJobRunShell(TriggerFiredBundle bundle) + throws SchedulerException { + return new JTAJobRunShell(scheduler, bundle); } - /** - *

- * Called by the {@link org.quartz.core.QuartzSchedulerThread} - * to return instances of - * {@link org.quartz.core.JobRunShell}. - *

- */ - public void returnJobRunShell(JobRunShell jobRunShell) { - jobRunShell.passivate(); - } + } \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/ee/jta/UserTransactionHelper.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/ee/jta/UserTransactionHelper.java (.../UserTransactionHelper.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/ee/jta/UserTransactionHelper.java (.../UserTransactionHelper.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,26 +16,34 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package 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.UserTransaction; -import org.quartz.SchedulerConfigException; import org.quartz.SchedulerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** *

* 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 { - + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -45,7 +53,6 @@ */ public static final String DEFAULT_USER_TX_LOCATION = "java:comp/UserTransaction"; - /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -54,12 +61,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private InitialContext ctxt; - - private UserTransaction ut; - - private String userTxURL; - + private static String userTxURL = DEFAULT_USER_TX_LOCATION; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -69,20 +72,9 @@ */ /** - *

- * Create a UserTransactionHelper instance with the given settings. - *

+ * Do not allow the creation of an all static utility class. */ - public UserTransactionHelper(String userTxURL) - throws SchedulerConfigException { - - try { - ctxt = new InitialContext(); - } catch (Exception e) { - throw new SchedulerConfigException( - "JTAJobRunShellFactory initialization failed.", e); - } - setUserTxLocation(userTxURL); + private UserTransactionHelper() { } /* @@ -93,7 +85,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - public String getUserTxLocation() { + public static String getUserTxLocation() { return userTxURL; } @@ -102,20 +94,128 @@ * be found. If not set, the default value is "java:comp/UserTransaction" - * which works for nearly all application servers. */ - public void setUserTxLocation(String userTxURL) { - if (userTxURL == null) userTxURL = DEFAULT_USER_TX_LOCATION; + public static void setUserTxLocation(String userTxURL) { + if (userTxURL != null) { + UserTransactionHelper.userTxURL = userTxURL; + } + } - this.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(); + } + } - public UserTransaction lookup() throws SchedulerException { - try { - return (UserTransaction) ctxt.lookup(userTxURL); - } catch (Exception nse) { - throw new SchedulerException( + + /** + * 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.", - nse); + t); + } + + if (userTransaction == null) { + closeContext(); + throw new SchedulerException( + "UserTransactionHelper could not lookup/create UserTransaction from the InitialContext."); + } } - } -} \ No newline at end of file + /** + * 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. + */ + @Override + protected void finalize() throws Throwable { + try { + if (context != null) { + getLog().warn("UserTransaction was never returned to the UserTransactionHelper."); + closeContext(); + } + } finally { + super.finalize(); + } + } + + private static Logger getLog() { + return LoggerFactory.getLogger(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); + } + } +} Index: 3rdParty_sources/quartz/org/quartz/ee/servlet/QuartzInitializerListener.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/ee/servlet/QuartzInitializerListener.java (.../QuartzInitializerListener.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/ee/servlet/QuartzInitializerListener.java (.../QuartzInitializerListener.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2010 Terracotta, Inc. * * 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 @@ -15,16 +15,16 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.ee.servlet; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.quartz.Scheduler; +import org.quartz.SchedulerException; import org.quartz.impl.StdSchedulerFactory; /** @@ -37,56 +37,85 @@ * *
  *     <context-param>
- *         <param-name>config-file</param-name>
+ *         <param-name>quartz:config-file</param-name>
  *         <param-value>/some/path/my_quartz.properties</param-value>
  *     </context-param>
  *     <context-param>
- *         <param-name>shutdown-on-unload</param-name>
+ *         <param-name>quartz:shutdown-on-unload</param-name>
  *         <param-value>true</param-value>
  *     </context-param>
  *     <context-param>
- *         <param-name>start-scheduler-on-load</param-name>
+ *         <param-name>quartz:wait-on-shutdown</param-name>
  *         <param-value>true</param-value>
  *     </context-param>
- *
+ *     <context-param>
+ *         <param-name>quartz:start-on-load</param-name>
+ *         <param-value>true</param-value>
+ *     </context-param>
  *     
  *     <listener>
  *         <listener-class>
- *             org.quartz.ee.servlet.QuartzInitializerServletListener
+ *             org.quartz.ee.servlet.QuartzInitializerListener
  *         </listener-class>
  *     </listener>
  * 
* *

*

- * The init parameter 'config-file' can be used to specify the path (and + * The init parameter 'quartz:config-file' can be used to specify the path (and * filename) of your Quartz properties file. If you leave out this parameter, * the default ("quartz.properties") will be used. *

* *

- * The init parameter 'shutdown-on-unload' can be used to specify whether you - * want scheduler.shutdown() called when the servlet is unloaded (usually when + * The init parameter 'quartz:shutdown-on-unload' can be used to specify whether you + * want scheduler.shutdown() called when the listener is unloaded (usually when * the application server is being shutdown). Possible values are "true" or * "false". The default is "true". *

* *

- * The init parameter 'start-scheduler-on-load' can be used to specify whether - * you want the scheduler.start() method called when the servlet is first loaded. + * The init parameter 'quartz:wait-on-shutdown' has effect when + * 'quartz:shutdown-on-unload' is specified "true", and indicates whether you + * want scheduler.shutdown(true) called when the listener is unloaded (usually when + * the application server is being shutdown). Passing "true" to the shutdown() call + * causes the scheduler to wait for existing jobs to complete. Possible values are + * "true" or "false". The default is "false". + *

+ * + *

+ * The init parameter 'quartz:start-on-load' can be used to specify whether + * you want the scheduler.start() method called when the listener is first loaded. * If set to false, your application will need to call the start() method before - * teh scheduler begins to run and process jobs. Possible values are "true" or + * the scheduler begins to run and process jobs. Possible values are "true" or * "false". The default is "true", which means the scheduler is started. *

* * A StdSchedulerFactory instance is stored into the ServletContext. You can gain access * to the factory from a ServletContext instance like this: *
- * + *
  * StdSchedulerFactory factory = (StdSchedulerFactory) ctx
- *				.getAttribute(QuartzFactoryServlet.QUARTZ_FACTORY_KEY);
- * 
- * 
+ * .getAttribute(QuartzInitializerListener.QUARTZ_FACTORY_KEY);
+ *

+ * The init parameter 'quartz:servlet-context-factory-key' can be used to override the + * name under which the StdSchedulerFactory is stored into the ServletContext, in + * which case you will want to use this name rather than + * QuartzInitializerListener.QUARTZ_FACTORY_KEY in the above example. + *

+ * + *

+ * The init parameter 'quartz:scheduler-context-servlet-context-key' if set, the + * ServletContext will be stored in the SchedulerContext under the given key + * name (and will therefore be available to jobs during execution). + *

+ * + *

+ * The init parameter 'quartz:start-delay-seconds' can be used to specify the amount + * of time to wait after initializing the scheduler before scheduler.start() + * is called. + *

+ * * Once you have the factory instance, you can retrieve the Scheduler instance by calling * getScheduler() on the factory. * @@ -96,83 +125,144 @@ */ public class QuartzInitializerListener implements ServletContextListener { - public static final String QUARTZ_FACTORY_KEY = "org.quartz.impl.StdSchedulerFactory.KEY"; + public static final String QUARTZ_FACTORY_KEY = "org.quartz.impl.StdSchedulerFactory.KEY"; - private boolean performShutdown = true; + private boolean performShutdown = true; + private boolean waitOnShutdown = false; - private Scheduler scheduler = null; + private Scheduler scheduler = null; - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ + private final Logger log = LoggerFactory.getLogger(getClass()); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ - public void contextInitialized(ServletContextEvent sce) { + public void contextInitialized(ServletContextEvent sce) { - System.out.println("Quartz Initializer Servlet loaded, initializing Scheduler..."); + log.info("Quartz Initializer Servlet loaded, initializing Scheduler..."); - ServletContext servletContext = sce.getServletContext(); - StdSchedulerFactory factory; - try { + ServletContext servletContext = sce.getServletContext(); + StdSchedulerFactory factory; + try { - String configFile = servletContext.getInitParameter("config-file"); - String shutdownPref = servletContext.getInitParameter("shutdown-on-unload"); + String configFile = servletContext.getInitParameter("quartz:config-file"); + if(configFile == null) + configFile = servletContext.getInitParameter("config-file"); // older name, for backward compatibility + String shutdownPref = servletContext.getInitParameter("quartz:shutdown-on-unload"); + if(shutdownPref == null) + shutdownPref = servletContext.getInitParameter("shutdown-on-unload"); + if (shutdownPref != null) { + performShutdown = Boolean.valueOf(shutdownPref).booleanValue(); + } + String shutdownWaitPref = servletContext.getInitParameter("quartz:wait-on-shutdown"); + if (shutdownPref != null) { + waitOnShutdown = Boolean.valueOf(shutdownWaitPref).booleanValue(); + } - if (shutdownPref != null) - performShutdown = Boolean.valueOf(shutdownPref).booleanValue(); + factory = getSchedulerFactory(configFile); - // get Properties - if (configFile != null) { - factory = new StdSchedulerFactory(configFile); - } else { - factory = new StdSchedulerFactory(); - } + // Always want to get the scheduler, even if it isn't starting, + // to make sure it is both initialized and registered. + scheduler = factory.getScheduler(); - // Should the Scheduler being started now or later - String startOnLoad = servletContext - .getInitParameter("start-scheduler-on-load"); - /* - * If the "start-scheduler-on-load" init-parameter is not specified, - * the scheduler will be started. This is to maintain backwards - * compatability. - */ - if (startOnLoad == null || (Boolean.valueOf(startOnLoad).booleanValue())) { - // Start now - scheduler = factory.getScheduler(); - scheduler.start(); - System.out.println("Scheduler has been started..."); - } else { - System.out.println("Scheduler has not been started. Use scheduler.start()"); - } + // Should the Scheduler being started now or later + String startOnLoad = servletContext.getInitParameter("quartz:start-on-load"); + if(startOnLoad == null) + startOnLoad = servletContext.getInitParameter("start-scheduler-on-load"); - System.out.println("Storing the Quartz Scheduler Factory in the servlet context at key: " - + QUARTZ_FACTORY_KEY); - servletContext.setAttribute(QUARTZ_FACTORY_KEY, factory); + int startDelay = 0; + String startDelayS = servletContext.getInitParameter("quartz:start-delay-seconds"); + if(startDelayS == null) + startDelayS = servletContext.getInitParameter("start-delay-seconds"); + try { + if(startDelayS != null && startDelayS.trim().length() > 0) + startDelay = Integer.parseInt(startDelayS); + } catch(Exception e) { + log.error("Cannot parse value of 'start-delay-seconds' to an integer: " + startDelayS + ", defaulting to 5 seconds."); + startDelay = 5; + } - } catch (Exception e) { - System.out.println("Quartz Scheduler failed to initialize: " + e.toString()); - e.printStackTrace(); - } - } + /* + * If the "quartz:start-on-load" init-parameter is not specified, + * the scheduler will be started. This is to maintain backwards + * compatability. + */ + if (startOnLoad == null || (Boolean.valueOf(startOnLoad).booleanValue())) { + if(startDelay <= 0) { + // Start now + scheduler.start(); + log.info("Scheduler has been started..."); + } + else { + // Start delayed + scheduler.startDelayed(startDelay); + log.info("Scheduler will start in " + startDelay + " seconds."); + } + } else { + log.info("Scheduler has not been started. Use scheduler.start()"); + } - public void contextDestroyed(ServletContextEvent sce) { + String factoryKey = servletContext.getInitParameter("quartz:servlet-context-factory-key"); + if(factoryKey == null) + factoryKey = servletContext.getInitParameter("servlet-context-factory-key"); + if (factoryKey == null) { + factoryKey = QUARTZ_FACTORY_KEY; + } - if (!performShutdown) - return; + log.info("Storing the Quartz Scheduler Factory in the servlet context at key: " + + factoryKey); + servletContext.setAttribute(factoryKey, factory); + + + String servletCtxtKey = servletContext.getInitParameter("quartz:scheduler-context-servlet-context-key"); + if(servletCtxtKey == null) + servletCtxtKey = servletContext.getInitParameter("scheduler-context-servlet-context-key"); + if (servletCtxtKey != null) { + log.info("Storing the ServletContext in the scheduler context at key: " + + servletCtxtKey); + scheduler.getContext().put(servletCtxtKey, servletContext); + } - try { - if (scheduler != null) - scheduler.shutdown(); - } catch (Exception e) { - System.out.println("Quartz Scheduler failed to shutdown cleanly: " + e.toString()); - e.printStackTrace(); - } + } catch (Exception e) { + log.error("Quartz Scheduler failed to initialize: " + e.toString()); + e.printStackTrace(); + } + } - System.out.println("Quartz Scheduler successful shutdown."); - } + protected StdSchedulerFactory getSchedulerFactory(String configFile) + throws SchedulerException { + StdSchedulerFactory factory; + // get Properties + if (configFile != null) { + factory = new StdSchedulerFactory(configFile); + } else { + factory = new StdSchedulerFactory(); + } + return factory; + } + public void contextDestroyed(ServletContextEvent sce) { + if (!performShutdown) { + return; + } + + try { + if (scheduler != null) { + scheduler.shutdown(waitOnShutdown); + } + } catch (Exception e) { + log.error("Quartz Scheduler failed to shutdown cleanly: " + e.toString()); + e.printStackTrace(); + } + + log.info("Quartz Scheduler successful shutdown."); + } + + } Index: 3rdParty_sources/quartz/org/quartz/ee/servlet/QuartzInitializerServlet.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/ee/servlet/QuartzInitializerServlet.java (.../QuartzInitializerServlet.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/ee/servlet/QuartzInitializerServlet.java (.../QuartzInitializerServlet.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.ee.servlet; import java.io.IOException; @@ -29,6 +26,7 @@ import javax.servlet.http.HttpServletResponse; import org.quartz.Scheduler; +import org.quartz.SchedulerException; import org.quartz.impl.StdSchedulerFactory; /** @@ -37,6 +35,10 @@ * load-on-startup servlet in a web application. *

* + *

Using this start-up servlet may be preferred to using the {@link QuartzInitializerListener} + * in some situations - namely when you want to initialize more than one scheduler in the same + * application.

+ * *

* You'll want to add something like this to your WEB-INF/web.xml file: * @@ -62,12 +64,14 @@ * <param-name>shutdown-on-unload</param-name> * <param-value>true</param-value> * </init-param> - * * <init-param> + * <param-name>wait-on-shutdown</param-name> + * <param-value>true</param-value> + * </init-param> + * <init-param> * <param-name>start-scheduler-on-load</param-name> * <param-value>true</param-value> * </init-param> - * * </servlet> * * @@ -86,21 +90,47 @@ *

* *

+ * The init parameter 'wait-on-shutdown' has effect when + * 'shutdown-on-unload' is specified "true", and indicates whether you + * want scheduler.shutdown(true) called when the listener is unloaded (usually when + * the application server is being shutdown). Passing "true" to the shutdown() call + * causes the scheduler to wait for existing jobs to complete. Possible values are + * "true" or "false". The default is "false". + *

+ * + *

* The init parameter 'start-scheduler-on-load' can be used to specify whether * you want the scheduler.start() method called when the servlet is first loaded. * If set to false, your application will need to call the start() method before - * teh scheduler begins to run and process jobs. Possible values are "true" or + * the scheduler begins to run and process jobs. Possible values are "true" or * "false". The default is "true", which means the scheduler is started. *

* * A StdSchedulerFactory instance is stored into the ServletContext. You can gain access * to the factory from a ServletContext instance like this: *
- * - * StdSchedulerFactory factory = (StdSchedulerFactory) ctx - * .getAttribute(QuartzFactoryServlet.QUARTZ_FACTORY_KEY); - * - *
+ *
+ *     StdSchedulerFactory factory = (StdSchedulerFactory) ctx
+ *                .getAttribute(QuartzFactoryServlet.QUARTZ_FACTORY_KEY);
+ *

+ * The init parameter 'servlet-context-factory-key' can be used to override the + * name under which the StdSchedulerFactory is stored into the ServletContext, in + * which case you will want to use this name rather than + * QuartzFactoryServlet.QUARTZ_FACTORY_KEY in the above example. + *

+ * + *

+ * The init parameter 'scheduler-context-servlet-context-key' if set, the + * ServletContext will be stored in the SchedulerContext under the given key + * name (and will therefore be available to jobs during execution). + *

+ * + *

+ * The init parameter 'start-delay-seconds' can be used to specify the amount + * of time to wait after initializing the scheduler before scheduler.start() + * is called. + *

+ * * Once you have the factory instance, you can retrieve the Scheduler instance by calling * getScheduler() on the factory. * @@ -109,92 +139,151 @@ */ public class QuartzInitializerServlet extends HttpServlet { - public static final String QUARTZ_FACTORY_KEY = "org.quartz.impl.StdSchedulerFactory.KEY"; + /** + * + */ + private static final long serialVersionUID = 1L; - private boolean performShutdown = true; + public static final String QUARTZ_FACTORY_KEY = "org.quartz.impl.StdSchedulerFactory.KEY"; - private Scheduler scheduler = null; + private boolean performShutdown = true; + private boolean waitOnShutdown = false; - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ + private transient Scheduler scheduler = null; - public void init(ServletConfig cfg) throws javax.servlet.ServletException { - super.init(cfg); - log("Quartz Initializer Servlet loaded, initializing Scheduler..."); + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ - StdSchedulerFactory factory; - try { + @Override + public void init(ServletConfig cfg) throws javax.servlet.ServletException { + super.init(cfg); - String configFile = cfg.getInitParameter("config-file"); - String shutdownPref = cfg.getInitParameter("shutdown-on-unload"); + log("Quartz Initializer Servlet loaded, initializing Scheduler..."); - if (shutdownPref != null) - performShutdown = Boolean.valueOf(shutdownPref).booleanValue(); + StdSchedulerFactory factory; + try { - // get Properties - if (configFile != null) { - factory = new StdSchedulerFactory(configFile); - } else { - factory = new StdSchedulerFactory(); - } + String configFile = cfg.getInitParameter("config-file"); + String shutdownPref = cfg.getInitParameter("shutdown-on-unload"); - // Should the Scheduler being started now or later - String startOnLoad = cfg - .getInitParameter("start-scheduler-on-load"); - /* - * If the "start-scheduler-on-load" init-parameter is not specified, - * the scheduler will be started. This is to maintain backwards - * compatability. - */ - if (startOnLoad == null || (Boolean.valueOf(startOnLoad).booleanValue())) { - // Start now - scheduler = factory.getScheduler(); - scheduler.start(); - log("Scheduler has been started..."); - } else { - log("Scheduler has not been started. Use scheduler.start()"); - } + if (shutdownPref != null) { + performShutdown = Boolean.valueOf(shutdownPref).booleanValue(); + } + String shutdownWaitPref = cfg.getInitParameter("wait-on-shutdown"); + if (shutdownPref != null) { + waitOnShutdown = Boolean.valueOf(shutdownWaitPref).booleanValue(); + } - log("Storing the Quartz Scheduler Factory in the servlet context at key: " - + QUARTZ_FACTORY_KEY); - cfg.getServletContext().setAttribute(QUARTZ_FACTORY_KEY, factory); + factory = getSchedulerFactory(configFile); + + // Always want to get the scheduler, even if it isn't starting, + // to make sure it is both initialized and registered. + scheduler = factory.getScheduler(); + + // Should the Scheduler being started now or later + String startOnLoad = cfg + .getInitParameter("start-scheduler-on-load"); - } catch (Exception e) { - log("Quartz Scheduler failed to initialize: " + e.toString()); - throw new ServletException(e); - } - } + int startDelay = 0; + String startDelayS = cfg.getInitParameter("start-delay-seconds"); + try { + if(startDelayS != null && startDelayS.trim().length() > 0) + startDelay = Integer.parseInt(startDelayS); + } catch(Exception e) { + log("Cannot parse value of 'start-delay-seconds' to an integer: " + startDelayS + ", defaulting to 5 seconds.", e); + startDelay = 5; + } + + /* + * If the "start-scheduler-on-load" init-parameter is not specified, + * the scheduler will be started. This is to maintain backwards + * compatability. + */ + if (startOnLoad == null || (Boolean.valueOf(startOnLoad).booleanValue())) { + if(startDelay <= 0) { + // Start now + scheduler.start(); + log("Scheduler has been started..."); + } + else { + // Start delayed + scheduler.startDelayed(startDelay); + log("Scheduler will start in " + startDelay + " seconds."); + } + } else { + log("Scheduler has not been started. Use scheduler.start()"); + } - public void destroy() { + String factoryKey = cfg.getInitParameter("servlet-context-factory-key"); + if (factoryKey == null) { + factoryKey = QUARTZ_FACTORY_KEY; + } + + log("Storing the Quartz Scheduler Factory in the servlet context at key: " + + factoryKey); + cfg.getServletContext().setAttribute(factoryKey, factory); + + + String servletCtxtKey = cfg.getInitParameter("scheduler-context-servlet-context-key"); + if (servletCtxtKey != null) { + log("Storing the ServletContext in the scheduler context at key: " + + servletCtxtKey); + scheduler.getContext().put(servletCtxtKey, cfg.getServletContext()); + } - if (!performShutdown) - return; + } catch (Exception e) { + log("Quartz Scheduler failed to initialize: " + e.toString()); + throw new ServletException(e); + } + } - try { - if (scheduler != null) - scheduler.shutdown(); - } catch (Exception e) { - log("Quartz Scheduler failed to shutdown cleanly: " + e.toString()); - e.printStackTrace(); - } + protected StdSchedulerFactory getSchedulerFactory(String configFile) + throws SchedulerException { + StdSchedulerFactory factory; + // get Properties + if (configFile != null) { + factory = new StdSchedulerFactory(configFile); + } else { + factory = new StdSchedulerFactory(); + } + return factory; + } - log("Quartz Scheduler successful shutdown."); - } + @Override + public void destroy() { - public void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - } + if (!performShutdown) { + return; + } - public void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - } + try { + if (scheduler != null) { + scheduler.shutdown(waitOnShutdown); + } + } catch (Exception e) { + log("Quartz Scheduler failed to shutdown cleanly: " + e.toString()); + e.printStackTrace(); + } + log("Quartz Scheduler successful shutdown."); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + response.sendError(HttpServletResponse.SC_FORBIDDEN); + } + } Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/helpers/TriggerUtils.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/helpers/VersionPrinter.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/helpers/VersionPrinter.java (.../VersionPrinter.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/helpers/VersionPrinter.java (.../VersionPrinter.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,6 +1,6 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,9 +16,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.helpers; import org.quartz.core.QuartzScheduler; @@ -32,6 +29,12 @@ */ public class VersionPrinter { + /** + * Private constructor because this is a pure utility class. + */ + private VersionPrinter() { + } + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Index: 3rdParty_sources/quartz/org/quartz/helpers/package.html =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/helpers/package.html (.../package.html) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/helpers/package.html (.../package.html) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -8,8 +8,8 @@


-See the Quartz project - at Open Symphony for more information. +See the Quartz project + for more information. \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/impl/DefaultThreadExecutor.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/DefaultThreadExecutor.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/DefaultThreadExecutor.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,21 @@ +package org.quartz.impl; + +import org.quartz.spi.ThreadExecutor; + +/** + * Schedules work on a newly spawned thread. This is the default Quartz + * behavior. + * + * @author matt.accola + * @version $Revision$ $Date$ + */ +public class DefaultThreadExecutor implements ThreadExecutor { + + public void initialize() { + } + + public void execute(Thread thread) { + thread.start(); + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/DirectSchedulerFactory.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/DirectSchedulerFactory.java (.../DirectSchedulerFactory.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/DirectSchedulerFactory.java (.../DirectSchedulerFactory.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,131 +1,144 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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.impl; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; import org.quartz.core.JobRunShellFactory; import org.quartz.core.QuartzScheduler; import org.quartz.core.QuartzSchedulerResources; -import org.quartz.core.SchedulingContext; import org.quartz.simpl.CascadingClassLoadHelper; import org.quartz.simpl.RAMJobStore; import org.quartz.simpl.SimpleThreadPool; import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.JobStore; +import org.quartz.spi.SchedulerPlugin; +import org.quartz.spi.ThreadExecutor; import org.quartz.spi.ThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.Collection; - /** *

* A singleton implementation of {@link 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();
- * 
+ *  * 
// create the thread pool SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads, Thread.NORM_PRIORITY); threadPool.initialize(); * // create the job store JobStore jobStore = new RAMJobStore();
+ *
  *  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();
+ *
+ *  JobStoreTX jdbcJobStore = new JobStoreTX(); jdbcJobStore.setDataSource("someDatasource"); jdbcJobStore.setPostgresStyleBlobs(true); jdbcJobStore.setTablePrefix("QRTZ_"); jdbcJobStore.setInstanceId("My Instance");
  * 
- * + * * @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"; + private static final boolean DEFAULT_JMX_EXPORT = false; + + private static final String DEFAULT_JMX_OBJECTNAME = null; + + private static final DefaultThreadExecutor DEFAULT_THREAD_EXECUTOR = new DefaultThreadExecutor(); + + private static final int DEFAULT_BATCH_MAX_SIZE = 1; + + private static final long DEFAULT_BATCH_TIME_WINDOW = 0L; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Data members. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ private boolean initialized = false; private static DirectSchedulerFactory instance = new DirectSchedulerFactory(); + private final Logger log = LoggerFactory.getLogger(getClass()); + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Constructors. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private Log getLog() { - return LogFactory.getLog(DirectSchedulerFactory.class); + protected Logger getLog() { + return log; } /** @@ -136,9 +149,9 @@ /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Interface. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -149,35 +162,26 @@ /** * 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 { + 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 @@ -186,10 +190,9 @@ * if the remote scheduler could not be reached. */ public void createRemoteScheduler(String rmiHost, int rmiPort) - throws SchedulerException { + throws SchedulerException { createRemoteScheduler(DEFAULT_SCHEDULER_NAME, DEFAULT_INSTANCE_ID, rmiHost, rmiPort); - initialized = true; } /** @@ -198,7 +201,7 @@ * 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 @@ -210,27 +213,54 @@ * @throws SchedulerException * if the remote scheduler could not be reached. */ - protected void createRemoteScheduler(String schedulerName, + public void createRemoteScheduler(String schedulerName, String schedulerInstanceId, String rmiHost, int rmiPort) - throws SchedulerException { - SchedulingContext schedCtxt = new SchedulingContext(); - schedCtxt.setInstanceId(schedulerInstanceId); + throws SchedulerException { + createRemoteScheduler(schedulerName, + schedulerInstanceId, null, rmiHost, rmiPort); + } - String uid = QuartzSchedulerResources.getUniqueIdentifier( + /** + * 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 { + + String uid = (rmiBindName != null) ? rmiBindName : + QuartzSchedulerResources.getUniqueIdentifier( schedulerName, schedulerInstanceId); - RemoteScheduler remoteScheduler = new RemoteScheduler(schedCtxt, uid, - rmiHost, rmiPort); + RemoteScheduler remoteScheduler = new RemoteScheduler(uid, rmiHost, rmiPort); SchedulerRepository schedRep = SchedulerRepository.getInstance(); schedRep.bind(remoteScheduler); + initialized = true; } /** * 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 @@ -239,10 +269,9 @@ * if initialization failed */ public void createScheduler(ThreadPool threadPool, JobStore jobStore) - throws SchedulerException { + throws SchedulerException { createScheduler(DEFAULT_SCHEDULER_NAME, DEFAULT_INSTANCE_ID, threadPool, jobStore); - initialized = true; } /** @@ -251,7 +280,7 @@ * 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 @@ -265,15 +294,15 @@ */ public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore) - throws SchedulerException { + 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 @@ -297,37 +326,197 @@ String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort, long idleWaitTime, long dbFailureRetryInterval) - throws SchedulerException { + throws SchedulerException { + createScheduler(schedulerName, + schedulerInstanceId, threadPool, + jobStore, null, // plugins + rmiRegistryHost, rmiRegistryPort, + idleWaitTime, dbFailureRetryInterval, + DEFAULT_JMX_EXPORT, DEFAULT_JMX_OBJECTNAME); + } + + /** + * 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 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, + boolean jmxExport, String jmxObjectName) + throws SchedulerException { + createScheduler(schedulerName, schedulerInstanceId, threadPool, + DEFAULT_THREAD_EXECUTOR, jobStore, schedulerPluginMap, + rmiRegistryHost, rmiRegistryPort, idleWaitTime, + dbFailureRetryInterval, jmxExport, jmxObjectName); + } + + /** + * 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 threadExecutor + * The thread executor for executing jobs + * @param jobStore + * The type of job store + * @param schedulerPluginMap + * Map from a String plugin names to + * {@link 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, + ThreadExecutor threadExecutor, + JobStore jobStore, Map schedulerPluginMap, + String rmiRegistryHost, int rmiRegistryPort, + long idleWaitTime, long dbFailureRetryInterval, + boolean jmxExport, String jmxObjectName) + throws SchedulerException { + createScheduler(schedulerName, schedulerInstanceId, threadPool, + DEFAULT_THREAD_EXECUTOR, jobStore, schedulerPluginMap, + rmiRegistryHost, rmiRegistryPort, idleWaitTime, + dbFailureRetryInterval, jmxExport, jmxObjectName, DEFAULT_BATCH_MAX_SIZE, DEFAULT_BATCH_TIME_WINDOW); + } + + /** + * 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 threadExecutor + * The thread executor for executing jobs + * @param jobStore + * The type of job store + * @param schedulerPluginMap + * Map from a String plugin names to + * {@link 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. + * @param maxBatchSize + * The maximum batch size of triggers, when acquiring them + * @param batchTimeWindow + * The time window for which it is allowed to "pre-acquire" triggers to fire + * @throws SchedulerException + * if initialization failed + */ + public void createScheduler(String schedulerName, + String schedulerInstanceId, ThreadPool threadPool, + ThreadExecutor threadExecutor, + JobStore jobStore, Map schedulerPluginMap, + String rmiRegistryHost, int rmiRegistryPort, + long idleWaitTime, long dbFailureRetryInterval, + boolean jmxExport, String jmxObjectName, int maxBatchSize, long batchTimeWindow) + throws SchedulerException { // Currently only one run-shell factory is available... JobRunShellFactory jrsf = new StdJobRunShellFactory(); // Fire everything up // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - SchedulingContext schedCtxt = new SchedulingContext(); - schedCtxt.setInstanceId(schedulerInstanceId); + threadPool.initialize(); + QuartzSchedulerResources qrs = new QuartzSchedulerResources(); qrs.setName(schedulerName); qrs.setInstanceId(schedulerInstanceId); + SchedulerDetailsSetter.setDetails(threadPool, schedulerName, schedulerInstanceId); qrs.setJobRunShellFactory(jrsf); qrs.setThreadPool(threadPool); + qrs.setThreadExecutor(threadExecutor); qrs.setJobStore(jobStore); + qrs.setMaxBatchSize(maxBatchSize); + qrs.setBatchTimeWindow(batchTimeWindow); qrs.setRMIRegistryHost(rmiRegistryHost); qrs.setRMIRegistryPort(rmiRegistryPort); + qrs.setJMXExport(jmxExport); + if (jmxObjectName != null) { + qrs.setJMXObjectName(jmxObjectName); + } + + // add plugins + if (schedulerPluginMap != null) { + for (Iterator pluginIter = schedulerPluginMap.values().iterator(); pluginIter.hasNext();) { + qrs.addSchedulerPlugin(pluginIter.next()); + } + } - QuartzScheduler qs = new QuartzScheduler(qrs, schedCtxt, idleWaitTime, - dbFailureRetryInterval); + QuartzScheduler qs = new QuartzScheduler(qrs, idleWaitTime, dbFailureRetryInterval); ClassLoadHelper cch = new CascadingClassLoadHelper(); cch.initialize(); - + + SchedulerDetailsSetter.setDetails(jobStore, schedulerName, schedulerInstanceId); + jobStore.initialize(cch, qs.getSchedulerSignaler()); - Scheduler scheduler = new StdScheduler(qs, schedCtxt); + Scheduler scheduler = new StdScheduler(qs); - jrsf.initialize(scheduler, schedCtxt); + jrsf.initialize(scheduler); + qs.initialize(); + + + // Initialize plugins now that we have a Scheduler instance. + if (schedulerPluginMap != null) { + for (Iterator> pluginEntryIter = schedulerPluginMap.entrySet().iterator(); pluginEntryIter.hasNext();) { + Entry pluginEntry = pluginEntryIter.next(); + + pluginEntry.getValue().initialize(pluginEntry.getKey(), scheduler, cch); + } + } + getLog().info("Quartz scheduler '" + scheduler.getSchedulerName()); getLog().info("Quartz scheduler version: " + qs.getVersion()); @@ -338,6 +527,8 @@ // garbage collected schedRep.bind(scheduler); + + initialized = true; } /* @@ -352,18 +543,19 @@ *

* 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()"); } - SchedulerRepository schedRep = SchedulerRepository.getInstance(); + if (!initialized) { + throw new SchedulerException( + "you must call createRemoteScheduler or createScheduler methods before calling getScheduler()"); + } - return schedRep.lookup(DEFAULT_SCHEDULER_NAME); + return getScheduler(DEFAULT_SCHEDULER_NAME); } /** @@ -383,7 +575,7 @@ * StdSchedulerFactory instance.). *

*/ - public Collection getAllSchedulers() throws SchedulerException { + public Collection getAllSchedulers() throws SchedulerException { return SchedulerRepository.getInstance().lookupAll(); } Index: 3rdParty_sources/quartz/org/quartz/impl/JobDetailImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/JobDetailImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/JobDetailImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,460 @@ + +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.Job; +import org.quartz.JobBuilder; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobKey; +import org.quartz.PersistJobDataAfterExecution; +import org.quartz.Scheduler; +import org.quartz.StatefulJob; +import org.quartz.Trigger; +import org.quartz.utils.ClassUtils; + + +/** + *

+ * 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. + *

+ * + *

+ * Jobs have a name and group associated with them, which + * should uniquely identify them within a single {@link Scheduler}. + *

+ * + *

+ * Triggers are the 'mechanism' by which Jobs + * are scheduled. Many Triggers can point to the same Job, + * but a single Trigger can only point to one Job. + *

+ * + * @see Job + * @see StatefulJob + * @see JobDataMap + * @see Trigger + * + * @author James House + * @author Sharada Jambula + */ +@SuppressWarnings("deprecation") +public class JobDetailImpl implements Cloneable, java.io.Serializable, JobDetail { + + private static final long serialVersionUID = -6069784757781506897L; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private String name; + + private String group = Scheduler.DEFAULT_GROUP; + + private String description; + + private Class jobClass; + + private JobDataMap jobDataMap; + + private boolean durability = false; + + private boolean shouldRecover = false; + + private transient JobKey key = null; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * 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 JobDetailImpl() { + // do nothing... + } + + /** + *

+ * Create a JobDetail with the given name, given class, default group, + * and the default settings of all the other properties. + *

+ * + * @exception IllegalArgumentException + * if name is null or empty, or the group is an empty string. + * + * @deprecated use {@link JobBuilder} + */ + public JobDetailImpl(String name, Class jobClass) { + this(name, null, jobClass); + } + + /** + *

+ * Create a JobDetail with the given name, group and class, + * and the default settings of all the other properties. + *

+ * + * @param group if null, Scheduler.DEFAULT_GROUP will be used. + * + * @exception IllegalArgumentException + * if name is null or empty, or the group is an empty string. + * + * @deprecated use {@link JobBuilder} + */ + public JobDetailImpl(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. + *

+ * + * @param group if null, Scheduler.DEFAULT_GROUP will be used. + * + * @exception IllegalArgumentException + * if name is null or empty, or the group is an empty string. + * + * @deprecated use {@link JobBuilder} + */ + public JobDetailImpl(String name, String group, Class jobClass, + boolean durability, boolean recover) { + setName(name); + setGroup(group); + setJobClass(jobClass); + setDurability(durability); + setRequestsRecovery(recover); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Get the name of this Job. + *

+ */ + public String getName() { + return name; + } + + /** + *

+ * Set the name of this Job. + *

+ * + * @exception IllegalArgumentException + * if name is null or empty. + */ + public void setName(String name) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException("Job name cannot be empty."); + } + + this.name = name; + this.key = null; + } + + /** + *

+ * Get the group of this Job. + *

+ */ + public String getGroup() { + return group; + } + + /** + *

+ * Set the group of this Job. + *

+ * + * @param group if 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; + this.key = null; + } + + /** + *

+ * Returns the 'full name' of the JobDetail in the format + * "group.name". + *

+ */ + public String getFullName() { + return group + "." + name; + } + + /* (non-Javadoc) + * @see org.quartz.JobDetailI#getKey() + */ + public JobKey getKey() { + if(key == null) { + if(getName() == null) + return null; + key = new JobKey(getName(), getGroup()); + } + + return key; + } + + public void setKey(JobKey key) { + if(key == null) + throw new IllegalArgumentException("Key cannot be null!"); + + setName(key.getName()); + setGroup(key.getGroup()); + this.key = key; + } + + /* (non-Javadoc) + * @see org.quartz.JobDetailI#getDescription() + */ + public String getDescription() { + return description; + } + + /** + *

+ * 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. + *

+ */ + public void setDescription(String description) { + this.description = description; + } + + /* (non-Javadoc) + * @see org.quartz.JobDetailI#getJobClass() + */ + public Class getJobClass() { + return jobClass; + } + + /** + *

+ * Set the instance of Job that will be executed. + *

+ * + * @exception IllegalArgumentException + * if jobClass is null or the class is not a 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; + } + + /* (non-Javadoc) + * @see org.quartz.JobDetailI#getJobDataMap() + */ + public JobDataMap getJobDataMap() { + if (jobDataMap == null) { + jobDataMap = new JobDataMap(); + } + return jobDataMap; + } + + /** + *

+ * Set the JobDataMap to be associated with the Job. + *

+ */ + public void setJobDataMap(JobDataMap jobDataMap) { + this.jobDataMap = jobDataMap; + } + + /** + *

+ * 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. + *

+ */ + public void setDurability(boolean durability) { + this.durability = durability; + } + + /** + *

+ * 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. + *

+ * + * @see JobExecutionContext#isRecovering() + */ + public void setRequestsRecovery(boolean shouldRecover) { + this.shouldRecover = shouldRecover; + } + + /* (non-Javadoc) + * @see org.quartz.JobDetailI#isDurable() + */ + public boolean isDurable() { + return durability; + } + + /** + * @return whether the associated Job class carries the {@link PersistJobDataAfterExecution} annotation. + */ + public boolean isPersistJobDataAfterExecution() { + + return ClassUtils.isAnnotationPresent(jobClass, PersistJobDataAfterExecution.class); + } + + /** + * @return whether the associated Job class carries the {@link DisallowConcurrentExecution} annotation. + */ + public boolean isConcurrentExectionDisallowed() { + + return ClassUtils.isAnnotationPresent(jobClass, DisallowConcurrentExecution.class); + } + + /* (non-Javadoc) + * @see org.quartz.JobDetailI#requestsRecovery() + */ + public boolean requestsRecovery() { + return shouldRecover; + } + + /** + *

+ * Return a simple string representation of this object. + *

+ */ + @Override + public String toString() { + return "JobDetail '" + getFullName() + "': jobClass: '" + + ((getJobClass() == null) ? null : getJobClass().getName()) + + " concurrentExectionDisallowed: " + isConcurrentExectionDisallowed() + + " persistJobDataAfterExecution: " + isPersistJobDataAfterExecution() + + " isDurable: " + isDurable() + " requestsRecovers: " + requestsRecovery(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof JobDetail)) { + return false; + } + + JobDetail other = (JobDetail) obj; + + if(other.getKey() == null || getKey() == null) + return false; + + if (!other.getKey().equals(getKey())) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public Object clone() { + JobDetailImpl copy; + try { + copy = (JobDetailImpl) super.clone(); + if (jobDataMap != null) { + copy.jobDataMap = (JobDataMap) jobDataMap.clone(); + } + } catch (CloneNotSupportedException ex) { + throw new IncompatibleClassChangeError("Not Cloneable."); + } + + return copy; + } + + public JobBuilder getJobBuilder() { + JobBuilder b = JobBuilder.newJob() + .ofType(getJobClass()) + .requestRecovery(requestsRecovery()) + .storeDurably(isDurable()) + .usingJobData(getJobDataMap()) + .withDescription(getDescription()) + .withIdentity(getKey()); + return b; + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/JobExecutionContextImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/JobExecutionContextImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/JobExecutionContextImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,273 @@ + +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl; + +import java.util.Date; +import java.util.HashMap; + +import org.quartz.Calendar; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.Scheduler; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.quartz.spi.OperableTrigger; +import org.quartz.spi.TriggerFiredBundle; + + +public class JobExecutionContextImpl implements java.io.Serializable, JobExecutionContext { + + private static final long serialVersionUID = -8139417614523942021L; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private transient Scheduler scheduler; + + private Trigger trigger; + + private JobDetail jobDetail; + + private JobDataMap jobDataMap; + + private transient Job job; + + private Calendar calendar; + + private boolean recovering = false; + + private int numRefires = 0; + + private Date fireTime; + + private Date scheduledFireTime; + + private Date prevFireTime; + + private Date nextFireTime; + + private long jobRunTime = -1; + + private Object result; + + private HashMap data = new HashMap(); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Create a JobExcecutionContext with the given context data. + *

+ */ + public JobExecutionContextImpl(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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * {@inheritDoc} + */ + public Scheduler getScheduler() { + return scheduler; + } + + /** + * {@inheritDoc} + */ + public Trigger getTrigger() { + return trigger; + } + + /** + * {@inheritDoc} + */ + public Calendar getCalendar() { + return calendar; + } + + /** + * {@inheritDoc} + */ + public boolean isRecovering() { + return recovering; + } + + public TriggerKey getRecoveringTriggerKey() { + if (isRecovering()) { + return new TriggerKey(jobDataMap.getString(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_GROUP), + jobDataMap.getString(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_NAME)); + } else { + throw new IllegalStateException("Not a recovering job"); + } + } + + public void incrementRefireCount() { + numRefires++; + } + + /** + * {@inheritDoc} + */ + public int getRefireCount() { + return numRefires; + } + + /** + * {@inheritDoc} + */ + public JobDataMap getMergedJobDataMap() { + return jobDataMap; + } + + /** + * {@inheritDoc} + */ + public JobDetail getJobDetail() { + return jobDetail; + } + + /** + * {@inheritDoc} + */ + public Job getJobInstance() { + return job; + } + + /** + * {@inheritDoc} + */ + public Date getFireTime() { + return fireTime; + } + + /** + * {@inheritDoc} + */ + public Date getScheduledFireTime() { + return scheduledFireTime; + } + + /** + * {@inheritDoc} + */ + public Date getPreviousFireTime() { + return prevFireTime; + } + + /** + * {@inheritDoc} + */ + public Date getNextFireTime() { + return nextFireTime; + } + + @Override + public String toString() { + return "JobExecutionContext:" + " trigger: '" + + getTrigger().getKey() + " job: " + + getJobDetail().getKey() + " fireTime: '" + getFireTime() + + " scheduledFireTime: " + getScheduledFireTime() + + " previousFireTime: '" + getPreviousFireTime() + + " nextFireTime: " + getNextFireTime() + " isRecovering: " + + isRecovering() + " refireCount: " + getRefireCount(); + } + + /** + * {@inheritDoc} + */ + public Object getResult() { + return result; + } + + /** + * {@inheritDoc} + */ + public void setResult(Object result) { + this.result = result; + } + + /** + * {@inheritDoc} + */ + public long getJobRunTime() { + return jobRunTime; + } + + /** + * @param jobRunTime The jobRunTime to set. + */ + public void setJobRunTime(long jobRunTime) { + this.jobRunTime = jobRunTime; + } + + /** + * {@inheritDoc} + */ + public void put(Object key, Object value) { + data.put(key, value); + } + + /** + * {@inheritDoc} + */ + public Object get(Object key) { + return data.get(key); + } + + /** + * {@inheritDoc} + */ + public String getFireInstanceId() { + return ((OperableTrigger)trigger).getFireInstanceId(); + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/QuartzServer.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/QuartzServer.java (.../QuartzServer.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/QuartzServer.java (.../QuartzServer.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl; import java.io.BufferedReader; @@ -26,52 +23,52 @@ import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.SchedulerFactory; -import org.quartz.Trigger; +import 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 org.quartz.JobListener}, * {@link org.quartz.TriggerListener} and {@link 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 implements org.quartz.SchedulerListener { +public class QuartzServer extends SchedulerListenerSupport { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Data members. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ private Scheduler sched = null; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Constructors. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -80,14 +77,14 @@ /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Interface. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ public void serve(SchedulerFactory schedFact, boolean console) - throws Exception { + throws Exception { sched = schedFact.getScheduler(); sched.start(); @@ -125,111 +122,27 @@ /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * SchedulerListener Interface. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** *

- * Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} - * is scheduled. - *

- */ - public void jobScheduled(Trigger trigger) { - } - - /** - *

- * Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} - * is unscheduled. - *

- */ - public void jobUnscheduled(String triggerName, String triggerGroup) { - } - - /** - *

- * Called by the {@link Scheduler} when a {@link Trigger} - * has reached the condition in which it will never fire again. - *

- */ - public void triggerFinalized(Trigger trigger) { - } - - /** - *

- * 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. - *

- */ - public void triggersPaused(String triggerName, String triggerGroup) { - } - - /** - *

- * 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. - *

- */ - public void triggersResumed(String triggerName, String triggerGroup) { - } - - /** - *

- * Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} - * or group of {@link org.quartz.JobDetail}s has been - * paused. - *

- * - *

- * If a group was paused, then the jobName parameter will be - * null. - *

- */ - public void jobsPaused(String jobName, String jobGroup) { - } - - /** - *

- * Called by the {@link Scheduler} when a {@link org.quartz.JobDetail} - * or group of {@link org.quartz.JobDetail}s has been - * un-paused. - *

- * - *

- * If a group was paused, then the jobName parameter will be - * null. - *

- */ - public void jobsResumed(String jobName, String jobGroup) { - } - - /** - *

* 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. *

*/ + @Override public void schedulerError(String msg, SchedulerException cause) { System.err.println("*** " + msg); cause.printStackTrace(); @@ -241,16 +154,17 @@ * that it has shutdown. *

*/ + @Override public void schedulerShutdown() { System.out.println("\n*** The scheduler is now shutdown."); sched = null; } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Main Method. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -266,11 +180,12 @@ try { QuartzServer server = new QuartzServer(); - if (args.length == 0) server.serve( + if (args.length == 0) { + server.serve( new org.quartz.impl.StdSchedulerFactory(), false); - else if (args.length == 1 && args[0].equalsIgnoreCase("console")) server - .serve(new org.quartz.impl.StdSchedulerFactory(), true); - else { + } else if (args.length == 1 && args[0].equalsIgnoreCase("console")) { + server.serve(new org.quartz.impl.StdSchedulerFactory(), true); + } else { System.err.println("\nUsage: QuartzServer [console]"); } } catch (Exception e) { Index: 3rdParty_sources/quartz/org/quartz/impl/RemoteMBeanScheduler.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/RemoteMBeanScheduler.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/RemoteMBeanScheduler.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,947 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl; + +import java.text.ParseException; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.openmbean.CompositeData; + +import org.quartz.Calendar; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobKey; +import org.quartz.ListenerManager; +import org.quartz.Scheduler; +import org.quartz.SchedulerContext; +import org.quartz.SchedulerException; +import org.quartz.SchedulerMetaData; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.quartz.UnableToInterruptJobException; +import org.quartz.Trigger.TriggerState; +import org.quartz.core.jmx.JobDetailSupport; +import org.quartz.core.jmx.TriggerSupport; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.impl.matchers.StringMatcher; +import 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. + *

+ * @see org.quartz.Scheduler + * @see org.quartz.core.QuartzScheduler + */ +public abstract class RemoteMBeanScheduler implements Scheduler { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + 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; + } + + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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", + "StandbyMode", + "Shutdown", + "JobStoreClassName", + "ThreadPoolClassName", + "ThreadPoolSize", + "Version", + "PerformanceMetrics" + }); + + try { + return new SchedulerMetaData( + (String)getAttribute(attributeList, 0).getValue(), + (String)getAttribute(attributeList, 1).getValue(), + getClass(), true, false, + (Boolean)getAttribute(attributeList, 2).getValue(), + (Boolean)getAttribute(attributeList, 3).getValue(), + null, + Integer.parseInt(((Map)getAttribute(attributeList, 8).getValue()).get("JobsExecuted").toString()), + Class.forName((String)getAttribute(attributeList, 4).getValue()), + false, + false, + Class.forName((String)getAttribute(attributeList, 5).getValue()), + (Integer)getAttribute(attributeList, 6).getValue(), + (String)getAttribute(attributeList, 7).getValue()); + } catch (ClassNotFoundException e) { + throw new SchedulerException(e); + } + } + + private Attribute getAttribute(AttributeList attributeList, int index) { + return (Attribute)attributeList.get(index); + } + + /** + *

+ * Returns the SchedulerContext of the Scheduler. + *

+ */ + public SchedulerContext getContext() throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /////////////////////////////////////////////////////////////////////////// + /// + /// 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[] {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[] {}); + } + + /** + * 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 (Boolean) getAttribute("Started"); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler. + *

+ */ + public boolean isInStandbyMode() throws SchedulerException { + return (Boolean)getAttribute("StandbyMode"); + } + + /** + *

+ * 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 { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler. + *

+ */ + public boolean isShutdown() throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler. + *

+ */ + @SuppressWarnings("unchecked") + public List getCurrentlyExecutingJobs() throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /////////////////////////////////////////////////////////////////////////// + /// + /// 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 { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public Date scheduleJob(Trigger trigger) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * 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[] { JobDetailSupport.toCompositeData(jobDetail), replace }, + new String[] { CompositeData.class.getName(), boolean.class.getName() }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void addJob(JobDetail jobDetail, boolean replace, boolean storeNonDurableWhileAwaitingScheduling) + throws SchedulerException { + invoke( + "addJob", + new Object[] { JobDetailSupport.toCompositeData(jobDetail), replace , storeNonDurableWhileAwaitingScheduling}, + new String[] { CompositeData.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 deleteJob(JobKey jobKey) + throws SchedulerException { + return (Boolean)invoke( + "deleteJob", + new Object[] { jobKey.getName(), jobKey.getGroup() }, + new String[] { String.class.getName(), String.class.getName() }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public boolean unscheduleJob(TriggerKey triggerKey) + throws SchedulerException { + return (Boolean)invoke( + "unscheduleJob", + new Object[] { triggerKey.getName(), triggerKey.getGroup() }, + new String[] { String.class.getName(), String.class.getName() }); + } + + + public boolean deleteJobs(List jobKeys) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + public void scheduleJobs(Map> triggersAndJobs, boolean replace) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + public void scheduleJob(JobDetail jobDetail, Set triggersForJob, boolean replace) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + public boolean unscheduleJobs(List triggerKeys) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public Date rescheduleJob(TriggerKey triggerKey, + Trigger newTrigger) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void triggerJob(JobKey jobKey) throws SchedulerException { + triggerJob(jobKey, null); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void triggerJob(JobKey jobKey, JobDataMap data) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void pauseTrigger(TriggerKey triggerKey) throws SchedulerException { + invoke( + "pauseTrigger", + new Object[] { triggerKey.getName(), triggerKey.getGroup() }, + new String[] { String.class.getName(), String.class.getName() }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void pauseTriggers(GroupMatcher matcher) throws SchedulerException { + String operation = null; + switch (matcher.getCompareWithOperator()) { + case EQUALS: + operation = "pauseTriggerGroup"; + break; + case CONTAINS: + operation = "pauseTriggersContaining"; + break; + case STARTS_WITH: + operation = "pauseTriggersStartingWith"; + break; + case ENDS_WITH: + operation = "pauseTriggersEndingWith"; + case ANYTHING: + operation = "pauseTriggersAll"; + } + + if (operation != null) { + invoke( + operation, + new Object[] { matcher.getCompareToValue() }, + new String[] { String.class.getName() }); + } else { + throw new SchedulerException("Unsupported GroupMatcher kind for pausing triggers: " + matcher.getCompareWithOperator()); + } + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void pauseJob(JobKey jobKey) throws SchedulerException { + invoke( + "pauseJob", + new Object[] { jobKey.getName(), jobKey.getGroup() }, + new String[] { String.class.getName(), String.class.getName() }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void pauseJobs(GroupMatcher matcher) throws SchedulerException { + String operation = null; + switch (matcher.getCompareWithOperator()) { + case EQUALS: + operation = "pauseJobGroup"; + break; + case STARTS_WITH: + operation = "pauseJobsStartingWith"; + break; + case ENDS_WITH: + operation = "pauseJobsEndingWith"; + break; + case CONTAINS: + operation = "pauseJobsContaining"; + case ANYTHING: + operation = "pauseJobsAll"; + } + + invoke( + operation, + new Object[] { matcher.getCompareToValue() }, + new String[] { String.class.getName() }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void resumeTrigger(TriggerKey triggerKey) + throws SchedulerException { + invoke( + "resumeTrigger", + new Object[] { triggerKey.getName(), triggerKey.getGroup() }, + new String[] { String.class.getName(), String.class.getName() }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void resumeTriggers(GroupMatcher matcher) throws SchedulerException { + String operation = null; + switch (matcher.getCompareWithOperator()) { + case EQUALS: + operation = "resumeTriggerGroup"; + break; + case CONTAINS: + operation = "resumeTriggersContaining"; + break; + case STARTS_WITH: + operation = "resumeTriggersStartingWith"; + break; + case ENDS_WITH: + operation = "resumeTriggersEndingWith"; + case ANYTHING: + operation = "resumeTriggersAll"; + } + + if (operation != null) { + invoke( + operation, + new Object[] { matcher.getCompareToValue() }, + new String[] { String.class.getName() }); + } else { + throw new SchedulerException("Unsupported GroupMatcher kind for resuming triggers: " + matcher.getCompareWithOperator()); + } + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void resumeJob(JobKey jobKey) + throws SchedulerException { + invoke( + "resumeJob", + new Object[] { jobKey.getName(), jobKey.getGroup() }, + new String[] { String.class.getName(), String.class.getName() }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void resumeJobs(GroupMatcher matcher) throws SchedulerException { + String operation = null; + switch (matcher.getCompareWithOperator()) { + case EQUALS: + operation = "resumeJobGroup"; + break; + case STARTS_WITH: + operation = "resumeJobsStartingWith"; + break; + case ENDS_WITH: + operation = "resumeJobsEndingWith"; + break; + case CONTAINS: + operation = "resumeJobsContaining"; + case ANYTHING: + operation = "resumeJobsAll"; + } + + invoke( + operation, + new Object[] { matcher.getCompareToValue() }, + new String[] { String.class.getName() }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void pauseAll() throws SchedulerException { + invoke( + "pauseAllTriggers", + new Object[] { }, + new String[] { }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public void resumeAll() throws SchedulerException { + invoke( + "resumeAllTriggers", + new Object[] { }, + new String[] { }); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + @SuppressWarnings("unchecked") + public List getJobGroupNames() throws SchedulerException { + return (List)getAttribute("JobGroupNames"); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + @SuppressWarnings("unchecked") + public Set getJobKeys(GroupMatcher matcher) throws SchedulerException { + if (matcher.getCompareWithOperator().equals(StringMatcher.StringOperatorName.EQUALS)) { + List keys = (List)invoke( + "getJobNames", + new Object[] { matcher.getCompareToValue() }, + new String[] { String.class.getName() }); + + return new HashSet(keys); + } else { + throw new SchedulerException("Only equals matcher are supported for looking up JobKeys"); + } + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + @SuppressWarnings("unchecked") + public List getTriggersOfJob(JobKey jobKey) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + @SuppressWarnings("unchecked") + public List getTriggerGroupNames() throws SchedulerException { + return (List)getAttribute("TriggerGroupNames"); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + @SuppressWarnings("unchecked") + public Set getTriggerKeys(GroupMatcher matcher) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public JobDetail getJobDetail(JobKey jobKey) throws SchedulerException { + try { + return JobDetailSupport.newJobDetail((CompositeData)invoke( + "getJobDetail", + new Object[] { jobKey.getName(), jobKey.getGroup() }, + new String[] { String.class.getName(), String.class.getName() })); + } catch (ClassNotFoundException e) { + throw new SchedulerException("Unable to resolve job class", e); + } + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler. + *

+ */ + public Trigger getTrigger(TriggerKey triggerKey) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler. + *

+ */ + public boolean checkExists(JobKey jobKey) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler. + *

+ */ + public boolean checkExists(TriggerKey triggerKey) throws SchedulerException { + return (Boolean)invoke( + "checkExists", + new Object[] { triggerKey }, + new String[] { TriggerKey.class.getName() }); + } + + public void clear() throws SchedulerException { + invoke( + "clear", + new Object[] { }, + new String[] { }); + } + + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public TriggerState getTriggerState(TriggerKey triggerKey) + throws SchedulerException { + return TriggerState.valueOf((String)invoke( + "getTriggerState", + new Object[] { triggerKey.getName(), triggerKey.getGroup() }, + new String[] { String.class.getName(), String.class.getName() })); + } + + /** + *

+ * 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[] { calName, calendar, replace, updateTriggers }, + new String[] { 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 { + invoke("deleteCalendar", + new Object[] { calName }, + new String[] { String.class.getName() }); + return true; + } + + /** + *

+ * Calls th0e equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + public Calendar getCalendar(String calName) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler, + * passing the SchedulingContext associated with this + * instance. + *

+ */ + @SuppressWarnings("unchecked") + public List getCalendarNames() throws SchedulerException { + return (List)getAttribute("CalendarNames"); + } + + /** + * @see org.quartz.Scheduler#getPausedTriggerGroups() + */ + @SuppressWarnings("unchecked") + public Set getPausedTriggerGroups() throws SchedulerException { + return (Set)getAttribute("PausedTriggerGroups"); + } + + /////////////////////////////////////////////////////////////////////////// + /// + /// Other Methods + /// + /////////////////////////////////////////////////////////////////////////// + + /** + *

+ * Calls the equivalent method on the 'proxied' QuartzScheduler. + *

+ */ + public ListenerManager getListenerManager() throws SchedulerException { + throw new SchedulerException( + "Operation not supported for remote schedulers."); + } + + /** + * @see org.quartz.Scheduler#interrupt(JobKey) + */ + public boolean interrupt(JobKey jobKey) throws UnableToInterruptJobException { + try { + return (Boolean)invoke( + "interruptJob", + new Object[] { jobKey.getName(), jobKey.getGroup() }, + new String[] { String.class.getName(), String.class.getName() }); + } catch (SchedulerException se) { + throw new UnableToInterruptJobException(se); + } + } + + + + public boolean interrupt(String fireInstanceId) throws UnableToInterruptJobException { + try { + return (Boolean)invoke( + "interruptJob", + new Object[] { fireInstanceId }, + new String[] { String.class.getName() }); + } catch (SchedulerException se) { + throw new UnableToInterruptJobException(se); + } + } + + /** + * @see org.quartz.Scheduler#setJobFactory(org.quartz.spi.JobFactory) + */ + public void setJobFactory(JobFactory factory) throws SchedulerException { + throw new SchedulerException("Operation not supported for remote schedulers."); + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/RemoteScheduler.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/RemoteScheduler.java (.../RemoteScheduler.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/RemoteScheduler.java (.../RemoteScheduler.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,32 +15,32 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package 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.Map; import java.util.Set; import org.quartz.Calendar; import org.quartz.JobDataMap; import org.quartz.JobDetail; -import org.quartz.JobListener; +import org.quartz.JobExecutionContext; +import org.quartz.JobKey; +import org.quartz.ListenerManager; import org.quartz.Scheduler; import org.quartz.SchedulerContext; import org.quartz.SchedulerException; -import org.quartz.SchedulerListener; import org.quartz.SchedulerMetaData; import org.quartz.Trigger; -import org.quartz.TriggerListener; +import org.quartz.TriggerKey; import org.quartz.UnableToInterruptJobException; +import org.quartz.Trigger.TriggerState; import org.quartz.core.RemotableQuartzScheduler; -import org.quartz.core.SchedulingContext; +import org.quartz.impl.matchers.GroupMatcher; import org.quartz.spi.JobFactory; /** @@ -52,7 +52,6 @@ * * @see org.quartz.Scheduler * @see org.quartz.core.QuartzScheduler - * @see org.quartz.core.SchedulingContext * * @author James House */ @@ -68,8 +67,6 @@ private RemotableQuartzScheduler rsched; - private SchedulingContext schedCtxt; - private String schedId; private String rmiHost; @@ -91,10 +88,7 @@ * SchedulingContext. *

*/ - public RemoteScheduler(SchedulingContext schedCtxt, String schedId, - String host, int port) { - - this.schedCtxt = schedCtxt; + public RemoteScheduler(String schedId, String host, int port) { this.schedId = schedId; this.rmiHost = host; this.rmiPort = port; @@ -109,8 +103,10 @@ */ protected RemotableQuartzScheduler getRemoteScheduler() - throws SchedulerException { - if (rsched != null) return rsched; + throws SchedulerException { + if (rsched != null) { + return rsched; + } try { Registry registry = LocateRegistry.getRegistry(rmiHost, rmiPort); @@ -121,8 +117,6 @@ SchedulerException initException = new SchedulerException( "Could not get handle to remote scheduler: " + e.getMessage(), e); - initException - .setErrorCode(SchedulerException.ERR_COMMUNICATION_FAILURE); throw initException; } @@ -133,7 +127,6 @@ Exception cause) { rsched = null; SchedulerException ex = new SchedulerException(msg, cause); - ex.setErrorCode(SchedulerException.ERR_COMMUNICATION_FAILURE); return ex; } @@ -169,12 +162,11 @@ try { RemotableQuartzScheduler sched = getRemoteScheduler(); return new SchedulerMetaData(getSchedulerName(), - getSchedulerInstanceId(), getClass(), true, sched - .runningSince() != null, isPaused(), isShutdown(), - sched.runningSince(), sched.numJobsExecuted(), sched - .getJobStoreClass(), sched.supportsPersistence(), - sched.getThreadPoolClass(), sched.getThreadPoolSize(), - sched.getVersion()); + getSchedulerInstanceId(), getClass(), true, isStarted(), + isInStandbyMode(), isShutdown(), sched.runningSince(), + sched.numJobsExecuted(), sched.getJobStoreClass(), + sched.supportsPersistence(), sched.isClustered(), sched.getThreadPoolClass(), + sched.getThreadPoolSize(), sched.getVersion()); } catch (RemoteException re) { throw invalidateHandleCreateException( @@ -222,6 +214,20 @@ * 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(); @@ -232,12 +238,27 @@ } /** - * @see 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); + } + } /** *

@@ -253,17 +274,18 @@ } } - 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); @@ -276,9 +298,13 @@ *

*/ public void shutdown(boolean waitForJobsToComplete) - throws SchedulerException { + throws SchedulerException { try { + String schedulerName = getSchedulerName(); + getRemoteScheduler().shutdown(waitForJobsToComplete); + + SchedulerRepository.getInstance().remove(schedulerName); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -304,7 +330,7 @@ * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public List getCurrentlyExecutingJobs() throws SchedulerException { + public List getCurrentlyExecutingJobs() throws SchedulerException { try { return getRemoteScheduler().getCurrentlyExecutingJobs(); } catch (RemoteException re) { @@ -321,15 +347,13 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ public Date scheduleJob(JobDetail jobDetail, Trigger trigger) - throws SchedulerException { + throws SchedulerException { try { - return getRemoteScheduler().scheduleJob(schedCtxt, jobDetail, + return getRemoteScheduler().scheduleJob(jobDetail, trigger); } catch (RemoteException re) { throw invalidateHandleCreateException( @@ -339,14 +363,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ public Date scheduleJob(Trigger trigger) throws SchedulerException { try { - return getRemoteScheduler().scheduleJob(schedCtxt, trigger); + return getRemoteScheduler().scheduleJob(trigger); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -355,51 +377,60 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ public void addJob(JobDetail jobDetail, boolean replace) - throws SchedulerException { + throws SchedulerException { try { - getRemoteScheduler().addJob(schedCtxt, jobDetail, replace); + getRemoteScheduler().addJob(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) + public void addJob(JobDetail jobDetail, boolean replace, boolean storeNonDurableWhileAwaitingScheduling) throws SchedulerException { try { - return getRemoteScheduler() - .deleteJob(schedCtxt, jobName, groupName); + getRemoteScheduler().addJob(jobDetail, replace, storeNonDurableWhileAwaitingScheduling); } 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) + public boolean deleteJobs(List jobKeys) throws SchedulerException { + try { + return getRemoteScheduler().deleteJobs(jobKeys); + } catch (RemoteException re) { + throw invalidateHandleCreateException( + "Error communicating with remote scheduler.", re); + } + } + + public void scheduleJobs(Map> triggersAndJobs, boolean replace) throws SchedulerException { + try { + getRemoteScheduler().scheduleJobs(triggersAndJobs, replace); + } catch (RemoteException re) { + throw invalidateHandleCreateException( + "Error communicating with remote scheduler.", re); + } + } + + public void scheduleJob(JobDetail jobDetail, Set triggersForJob, boolean replace) throws SchedulerException { + try { + getRemoteScheduler().scheduleJob(jobDetail, triggersForJob, replace); + } catch (RemoteException re) { + throw invalidateHandleCreateException( + "Error communicating with remote scheduler.", re); + } + } + + public boolean unscheduleJobs(List triggerKeys) throws SchedulerException { try { - return getRemoteScheduler().unscheduleJob(schedCtxt, triggerName, - groupName); + return getRemoteScheduler().unscheduleJobs(triggerKeys); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -408,76 +439,71 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Date rescheduleJob(String triggerName, - String groupName, Trigger newTrigger) throws SchedulerException { + public boolean deleteJob(JobKey jobKey) + throws SchedulerException { try { - return getRemoteScheduler().rescheduleJob(schedCtxt, triggerName, - groupName, newTrigger); + return getRemoteScheduler() + .deleteJob(jobKey); } 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. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void triggerJob(String jobName, String groupName) - throws SchedulerException { - triggerJob(jobName, groupName, null); + public boolean unscheduleJob(TriggerKey triggerKey) + throws SchedulerException { + try { + return getRemoteScheduler().unscheduleJob(triggerKey); + } 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. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void triggerJob(String jobName, String groupName, JobDataMap data) - throws SchedulerException { + public Date rescheduleJob(TriggerKey triggerKey, + Trigger newTrigger) throws SchedulerException { try { - getRemoteScheduler().triggerJob(schedCtxt, jobName, groupName, data); + return getRemoteScheduler().rescheduleJob(triggerKey, + 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. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void triggerJobWithVolatileTrigger(String jobName, String groupName) + public void triggerJob(JobKey jobKey) throws SchedulerException { - triggerJobWithVolatileTrigger(jobName, groupName, null); + triggerJob(jobKey, null); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void triggerJobWithVolatileTrigger(String jobName, String groupName, JobDataMap data) - throws SchedulerException { + public void triggerJob(JobKey jobKey, JobDataMap data) + throws SchedulerException { try { - getRemoteScheduler().triggerJobWithVolatileTrigger(schedCtxt, - jobName, groupName, data); + getRemoteScheduler().triggerJob(jobKey, data); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -486,16 +512,14 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void pauseTrigger(String triggerName, String groupName) - throws SchedulerException { + public void pauseTrigger(TriggerKey triggerKey) + throws SchedulerException { try { getRemoteScheduler() - .pauseTrigger(schedCtxt, triggerName, groupName); + .pauseTrigger(triggerKey); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -504,14 +528,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void pauseTriggerGroup(String groupName) throws SchedulerException { + public void pauseTriggers(GroupMatcher matcher) throws SchedulerException { try { - getRemoteScheduler().pauseTriggerGroup(schedCtxt, groupName); + getRemoteScheduler().pauseTriggers(matcher); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -520,15 +542,13 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void pauseJob(String jobName, String groupName) - throws SchedulerException { + public void pauseJob(JobKey jobKey) + throws SchedulerException { try { - getRemoteScheduler().pauseJob(schedCtxt, jobName, groupName); + getRemoteScheduler().pauseJob(jobKey); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -537,14 +557,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void pauseJobGroup(String groupName) throws SchedulerException { + public void pauseJobs(GroupMatcher matcher) throws SchedulerException { try { - getRemoteScheduler().pauseJobGroup(schedCtxt, groupName); + getRemoteScheduler().pauseJobs(matcher); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -553,16 +571,13 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void resumeTrigger(String triggerName, String groupName) - throws SchedulerException { + public void resumeTrigger(TriggerKey triggerKey) + throws SchedulerException { try { - getRemoteScheduler().resumeTrigger(schedCtxt, triggerName, - groupName); + getRemoteScheduler().resumeTrigger(triggerKey); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -571,14 +586,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void resumeTriggerGroup(String groupName) throws SchedulerException { + public void resumeTriggers(GroupMatcher matcher) throws SchedulerException { try { - getRemoteScheduler().resumeTriggerGroup(schedCtxt, groupName); + getRemoteScheduler().resumeTriggers(matcher); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -587,15 +600,13 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void resumeJob(String jobName, String groupName) - throws SchedulerException { + public void resumeJob(JobKey jobKey) + throws SchedulerException { try { - getRemoteScheduler().resumeJob(schedCtxt, jobName, groupName); + getRemoteScheduler().resumeJob(jobKey); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -604,14 +615,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void resumeJobGroup(String groupName) throws SchedulerException { + public void resumeJobs(GroupMatcher matcher) throws SchedulerException { try { - getRemoteScheduler().resumeJobGroup(schedCtxt, groupName); + getRemoteScheduler().resumeJobs(matcher); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -620,14 +629,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ public void pauseAll() throws SchedulerException { try { - getRemoteScheduler().pauseAll(schedCtxt); + getRemoteScheduler().pauseAll(); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -636,14 +643,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ public void resumeAll() throws SchedulerException { try { - getRemoteScheduler().resumeAll(schedCtxt); + getRemoteScheduler().resumeAll(); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -652,14 +657,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public String[] getJobGroupNames() throws SchedulerException { + public List getJobGroupNames() throws SchedulerException { try { - return getRemoteScheduler().getJobGroupNames(schedCtxt); + return getRemoteScheduler().getJobGroupNames(); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -668,14 +671,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public String[] getJobNames(String groupName) throws SchedulerException { + public Set getJobKeys(GroupMatcher matcher) throws SchedulerException { try { - return getRemoteScheduler().getJobNames(schedCtxt, groupName); + return getRemoteScheduler().getJobKeys(matcher); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -684,16 +685,13 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Trigger[] getTriggersOfJob(String jobName, String groupName) - throws SchedulerException { + public List getTriggersOfJob(JobKey jobKey) + throws SchedulerException { try { - return getRemoteScheduler().getTriggersOfJob(schedCtxt, jobName, - groupName); + return getRemoteScheduler().getTriggersOfJob(jobKey); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -702,14 +700,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public String[] getTriggerGroupNames() throws SchedulerException { + public List getTriggerGroupNames() throws SchedulerException { try { - return getRemoteScheduler().getTriggerGroupNames(schedCtxt); + return getRemoteScheduler().getTriggerGroupNames(); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -718,14 +714,12 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public String[] getTriggerNames(String groupName) throws SchedulerException { + public Set getTriggerKeys(GroupMatcher matcher) throws SchedulerException { try { - return getRemoteScheduler().getTriggerNames(schedCtxt, groupName); + return getRemoteScheduler().getTriggerKeys(matcher); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -734,16 +728,13 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public JobDetail getJobDetail(String jobName, String jobGroup) - throws SchedulerException { + public JobDetail getJobDetail(JobKey jobKey) + throws SchedulerException { try { - return getRemoteScheduler().getJobDetail(schedCtxt, jobName, - jobGroup); + return getRemoteScheduler().getJobDetail(jobKey); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -752,68 +743,55 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Trigger getTrigger(String triggerName, String triggerGroup) - throws SchedulerException { + public boolean checkExists(JobKey jobKey) throws SchedulerException { try { - return getRemoteScheduler().getTrigger(schedCtxt, triggerName, - triggerGroup); + return getRemoteScheduler().checkExists(jobKey); } 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. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public int getTriggerState(String triggerName, String triggerGroup) - throws SchedulerException { + public boolean checkExists(TriggerKey triggerKey) throws SchedulerException { try { - return getRemoteScheduler().getTriggerState(schedCtxt, triggerName, - triggerGroup); + return getRemoteScheduler().checkExists(triggerKey); } 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. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) - throws SchedulerException { + public void clear() throws SchedulerException { try { - getRemoteScheduler().addCalendar(schedCtxt, calName, calendar, - replace, updateTriggers); + getRemoteScheduler().clear(); } 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. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public boolean deleteCalendar(String calName) throws SchedulerException { + public Trigger getTrigger(TriggerKey triggerKey) + throws SchedulerException { try { - return getRemoteScheduler().deleteCalendar(schedCtxt, calName); + return getRemoteScheduler().getTrigger(triggerKey); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -822,14 +800,13 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Calendar getCalendar(String calName) throws SchedulerException { + public TriggerState getTriggerState(TriggerKey triggerKey) + throws SchedulerException { try { - return getRemoteScheduler().getCalendar(schedCtxt, calName); + return getRemoteScheduler().getTriggerState(triggerKey); } catch (RemoteException re) { throw invalidateHandleCreateException( "Error communicating with remote scheduler.", re); @@ -838,240 +815,104 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public String[] getCalendarNames() throws SchedulerException { + public void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) + throws SchedulerException { try { - return getRemoteScheduler().getCalendarNames(schedCtxt); + getRemoteScheduler().addCalendar(calName, calendar, + replace, updateTriggers); } 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); + public boolean deleteCalendar(String calName) throws SchedulerException { + try { + return getRemoteScheduler().deleteCalendar(calName); + } catch (RemoteException re) { + throw invalidateHandleCreateException( + "Error communicating with remote scheduler.", re); + } } /** *

* 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); + public Calendar getCalendar(String calName) throws SchedulerException { + try { + return getRemoteScheduler().getCalendar(calName); + } catch (RemoteException re) { + throw invalidateHandleCreateException( + "Error communicating with remote scheduler.", re); + } } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public boolean removeGlobalJobListener(JobListener jobListener) - throws SchedulerException { - throw new SchedulerException( - "Operation not supported for remote schedulers.", - SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION); + public List getCalendarNames() throws SchedulerException { + try { + return getRemoteScheduler().getCalendarNames(); + } catch (RemoteException re) { + throw invalidateHandleCreateException( + "Error communicating with remote scheduler.", re); + } } - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler. - *

+ /** + * @see org.quartz.Scheduler#getPausedTriggerGroups() */ - public boolean removeJobListener(String name) throws SchedulerException { - throw new SchedulerException( - "Operation not supported for remote schedulers.", - SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION); + public Set getPausedTriggerGroups() throws SchedulerException { + try { + return getRemoteScheduler().getPausedTriggerGroups(); + } catch (RemoteException re) { + throw invalidateHandleCreateException( + "Error communicating with remote scheduler.", re); + } } - /** - *

- * 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); - } + /////////////////////////////////////////////////////////////////////////// + /// + /// Other Methods + /// + /////////////////////////////////////////////////////////////////////////// - /** - *

- * 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 { + public ListenerManager getListenerManager() throws SchedulerException { throw new SchedulerException( - "Operation not supported for remote schedulers.", - SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION); + "Operation not supported for remote schedulers."); } /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler. - *

+ * @see org.quartz.Scheduler#interrupt(JobKey) */ - 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. - *

- */ - 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 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 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 org.quartz.Scheduler#getPausedTriggerGroups() - */ - public Set getPausedTriggerGroups() throws SchedulerException { + public boolean interrupt(JobKey jobKey) throws UnableToInterruptJobException { try { - return getRemoteScheduler().getPausedTriggerGroups(schedCtxt); + return getRemoteScheduler().interrupt(jobKey); } catch (RemoteException re) { - throw invalidateHandleCreateException( - "Error communicating with remote scheduler.", re); + throw new UnableToInterruptJobException(invalidateHandleCreateException( + "Error communicating with remote scheduler.", re)); + } catch (SchedulerException se) { + throw new UnableToInterruptJobException(se); } } - /** - * @see org.quartz.Scheduler#interrupt(java.lang.String, java.lang.String) - */ - public boolean interrupt(String jobName, String groupName) throws UnableToInterruptJobException { + public boolean interrupt(String fireInstanceId) throws UnableToInterruptJobException { try { - return getRemoteScheduler().interrupt(schedCtxt, jobName, groupName); + return getRemoteScheduler().interrupt(fireInstanceId); } catch (RemoteException re) { throw new UnableToInterruptJobException(invalidateHandleCreateException( "Error communicating with remote scheduler.", re)); @@ -1085,7 +926,7 @@ */ public void setJobFactory(JobFactory factory) throws SchedulerException { throw new SchedulerException( - "Operation not supported for remote schedulers.", - SchedulerException.ERR_UNSUPPORTED_FUNCTION_IN_THIS_CONFIGURATION); + "Operation not supported for remote schedulers."); } + } Index: 3rdParty_sources/quartz/org/quartz/impl/SchedulerDetailsSetter.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/SchedulerDetailsSetter.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/SchedulerDetailsSetter.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,68 @@ +package org.quartz.impl; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.quartz.SchedulerException; + +/** + * This utility calls methods reflectively on the given objects even though the + * methods are likely on a proper interface (ThreadPool, JobStore, etc). The + * motivation is to be tolerant of older implementations that have not been + * updated for the changes in the interfaces (eg. LocalTaskExecutorThreadPool in + * spring quartz helpers) + * + * @author teck + */ +class SchedulerDetailsSetter { + + private static final Logger LOGGER = LoggerFactory.getLogger(SchedulerDetailsSetter.class); + + private SchedulerDetailsSetter() { + // + } + + static void setDetails(Object target, String schedulerName, + String schedulerId) throws SchedulerException { + set(target, "setInstanceName", schedulerName); + set(target, "setInstanceId", schedulerId); + } + + private static void set(Object target, String method, String value) + throws SchedulerException { + final Method setter; + + try { + setter = target.getClass().getMethod(method, String.class); + } catch (SecurityException e) { + LOGGER.error("A SecurityException occured: " + e.getMessage(), e); + return; + } catch (NoSuchMethodException e) { + // This probably won't happen since the interface has the method + LOGGER.warn(target.getClass().getName() + + " does not contain public method " + method + "(String)"); + return; + } + + if (Modifier.isAbstract(setter.getModifiers())) { + // expected if method not implemented (but is present on + // interface) + LOGGER.warn(target.getClass().getName() + + " does not implement " + method + + "(String)"); + return; + } + + try { + setter.invoke(target, value); + } catch (InvocationTargetException ite) { + throw new SchedulerException(ite.getTargetException()); + } catch (Exception e) { + throw new SchedulerException(e); + } + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/SchedulerRepository.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/SchedulerRepository.java (.../SchedulerRepository.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/SchedulerRepository.java (.../SchedulerRepository.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl; import java.util.Collection; @@ -45,7 +42,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private HashMap schedulers; + private HashMap schedulers; private static SchedulerRepository inst; @@ -58,7 +55,7 @@ */ private SchedulerRepository() { - schedulers = new HashMap(); + schedulers = new HashMap(); } /* @@ -70,17 +67,19 @@ */ public static synchronized SchedulerRepository getInstance() { - if (inst == null) inst = new SchedulerRepository(); + 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); + if ((Scheduler) schedulers.get(sched.getSchedulerName()) != null) { + throw new SchedulerException("Scheduler with name '" + + sched.getSchedulerName() + "' already exists."); + } schedulers.put(sched.getSchedulerName(), sched); } @@ -90,10 +89,10 @@ } public synchronized Scheduler lookup(String schedName) { - return (Scheduler) schedulers.get(schedName); + return schedulers.get(schedName); } - public synchronized Collection lookupAll() { + public synchronized Collection lookupAll() { return java.util.Collections .unmodifiableCollection(schedulers.values()); } Index: 3rdParty_sources/quartz/org/quartz/impl/StdJobRunShellFactory.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/StdJobRunShellFactory.java (.../StdJobRunShellFactory.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/StdJobRunShellFactory.java (.../StdJobRunShellFactory.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,16 +15,13 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.core.JobRunShell; import org.quartz.core.JobRunShellFactory; -import org.quartz.core.SchedulingContext; +import org.quartz.spi.TriggerFiredBundle; /** *

@@ -33,11 +30,6 @@ * instance. *

* - *

- * This implementation does not re-use any objects, it simply makes a new - * JobRunShell each time borrowJobRunShell() is called. - *

- * * @author James House */ public class StdJobRunShellFactory implements JobRunShellFactory { @@ -51,8 +43,6 @@ private Scheduler scheduler; - private SchedulingContext schedCtxt; - /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -65,14 +55,11 @@ *

* 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. + * the JobExecutionContext s within it. *

*/ - public void initialize(Scheduler scheduler, SchedulingContext schedCtxt) { - this.scheduler = scheduler; - this.schedCtxt = schedCtxt; + public void initialize(Scheduler sched) { + this.scheduler = sched; } /** @@ -82,19 +69,7 @@ * {@link org.quartz.core.JobRunShell}. *

*/ - public JobRunShell borrowJobRunShell() throws SchedulerException { - return new JobRunShell(this, scheduler, schedCtxt); + public JobRunShell createJobRunShell(TriggerFiredBundle bndle) throws SchedulerException { + return new JobRunShell(scheduler, bndle); } - - /** - *

- * Called by the {@link org.quartz.core.QuartzSchedulerThread} - * to return instances of - * {@link org.quartz.core.JobRunShell}. - *

- */ - public void returnJobRunShell(JobRunShell jobRunShell) { - jobRunShell.passivate(); - } - } Index: 3rdParty_sources/quartz/org/quartz/impl/StdScheduler.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/StdScheduler.java (.../StdScheduler.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/StdScheduler.java (.../StdScheduler.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,29 +15,29 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Set; import org.quartz.Calendar; import org.quartz.JobDataMap; import org.quartz.JobDetail; -import org.quartz.JobListener; +import org.quartz.JobExecutionContext; +import org.quartz.JobKey; +import org.quartz.ListenerManager; import org.quartz.Scheduler; import org.quartz.SchedulerContext; import org.quartz.SchedulerException; -import org.quartz.SchedulerListener; import org.quartz.SchedulerMetaData; import org.quartz.Trigger; -import org.quartz.TriggerListener; +import org.quartz.TriggerKey; import org.quartz.UnableToInterruptJobException; +import org.quartz.Trigger.TriggerState; import org.quartz.core.QuartzScheduler; -import org.quartz.core.SchedulingContext; +import org.quartz.impl.matchers.GroupMatcher; import org.quartz.spi.JobFactory; /** @@ -49,8 +49,7 @@ * * @see org.quartz.Scheduler * @see org.quartz.core.QuartzScheduler - * @see org.quartz.core.SchedulingContext - * + * * @author James House */ public class StdScheduler implements Scheduler { @@ -65,8 +64,6 @@ private QuartzScheduler sched; - private SchedulingContext schedCtxt; - /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -81,9 +78,8 @@ * QuartzScheduler instance, and with the given SchedulingContext. *

*/ - public StdScheduler(QuartzScheduler sched, SchedulingContext schedCtxt) { + public StdScheduler(QuartzScheduler sched) { this.sched = sched; - this.schedCtxt = schedCtxt; } /* @@ -114,12 +110,11 @@ public SchedulerMetaData getMetaData() { return new SchedulerMetaData(getSchedulerName(), - getSchedulerInstanceId(), getClass(), false, sched - .runningSince() != null, isPaused(), isShutdown(), - sched.runningSince(), sched.numJobsExecuted(), sched - .getJobStoreClass(), sched.supportsPersistence(), sched - .getThreadPoolClass(), sched.getThreadPoolSize(), sched - .getVersion()); + getSchedulerInstanceId(), getClass(), false, isStarted(), + isInStandbyMode(), isShutdown(), sched.runningSince(), + sched.numJobsExecuted(), sched.getJobStoreClass(), + sched.supportsPersistence(), sched.isClustered(), sched.getThreadPoolClass(), + sched.getThreadPoolSize(), sched.getVersion()); } @@ -151,14 +146,12 @@ *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

- * - * @deprecated - * @see standby() */ - public void pause() { - this.standby(); + public void startDelayed(int seconds) throws SchedulerException { + sched.startDelayed(seconds); } - + + /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. @@ -169,7 +162,25 @@ } /** + * 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() { + return (sched.runningSince() != null); + } + + /** + *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ @@ -178,13 +189,6 @@ } /** - * @deprecated - */ - public boolean isPaused() { - return isInStandbyMode(); - } - - /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

@@ -216,7 +220,7 @@ * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public List getCurrentlyExecutingJobs() { + public List getCurrentlyExecutingJobs() { return sched.getCurrentlyExecutingJobs(); } @@ -228,549 +232,377 @@ /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Date scheduleJob(JobDetail jobDetail, Trigger trigger) - throws SchedulerException { - return sched.scheduleJob(schedCtxt, jobDetail, trigger); + public void clear() throws SchedulerException { + sched.clear(); } - - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public Date scheduleJob(Trigger trigger) throws SchedulerException { - return sched.scheduleJob(schedCtxt, trigger); - } - - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public void addJob(JobDetail jobDetail, boolean replace) - throws SchedulerException { - sched.addJob(schedCtxt, jobDetail, replace); - } - - /** - *

- * 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 sched.deleteJob(schedCtxt, jobName, groupName); - } - - /** - *

- * 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 sched.unscheduleJob(schedCtxt, triggerName, groupName); - } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Date rescheduleJob(String triggerName, - String groupName, Trigger newTrigger) throws SchedulerException { - return sched.rescheduleJob(schedCtxt, triggerName, groupName, newTrigger); + public Date scheduleJob(JobDetail jobDetail, Trigger trigger) + throws SchedulerException { + return sched.scheduleJob(jobDetail, trigger); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void triggerJob(String jobName, String groupName) - throws SchedulerException { - triggerJob(jobName, groupName, null); + public Date scheduleJob(Trigger trigger) throws SchedulerException { + return sched.scheduleJob(trigger); } - - /** - *

- * 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 { - sched.triggerJob(schedCtxt, jobName, groupName, data); - } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void triggerJobWithVolatileTrigger(String jobName, String groupName) - throws SchedulerException { - triggerJobWithVolatileTrigger(jobName, groupName, null); + public void addJob(JobDetail jobDetail, boolean replace) + throws SchedulerException { + sched.addJob(jobDetail, replace); } - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public void triggerJobWithVolatileTrigger(String jobName, String groupName, JobDataMap data) + public void addJob(JobDetail jobDetail, boolean replace, boolean storeNonDurableWhileAwaitingScheduling) throws SchedulerException { - sched.triggerJobWithVolatileTrigger(schedCtxt, jobName, groupName, data); + sched.addJob(jobDetail, replace, storeNonDurableWhileAwaitingScheduling); } - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public void pauseTrigger(String triggerName, String groupName) - throws SchedulerException { - sched.pauseTrigger(schedCtxt, triggerName, groupName); - } - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public void pauseTriggerGroup(String groupName) throws SchedulerException { - sched.pauseTriggerGroup(schedCtxt, groupName); + public boolean deleteJobs(List jobKeys) throws SchedulerException { + return sched.deleteJobs(jobKeys); } - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public void pauseJob(String jobName, String groupName) - throws SchedulerException { - sched.pauseJob(schedCtxt, jobName, groupName); + public void scheduleJobs(Map> triggersAndJobs, boolean replace) throws SchedulerException { + sched.scheduleJobs(triggersAndJobs, replace); } - /** - * @see org.quartz.Scheduler#getPausedTriggerGroups() - */ - public Set getPausedTriggerGroups() throws SchedulerException { - return sched.getPausedTriggerGroups(schedCtxt); + public void scheduleJob(JobDetail jobDetail, Set triggersForJob, boolean replace) throws SchedulerException { + sched.scheduleJob(jobDetail, triggersForJob, replace); } - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public void pauseJobGroup(String groupName) throws SchedulerException { - sched.pauseJobGroup(schedCtxt, groupName); - } - - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public void resumeTrigger(String triggerName, String groupName) + public boolean unscheduleJobs(List triggerKeys) throws SchedulerException { - sched.resumeTrigger(schedCtxt, triggerName, groupName); - } - + return sched.unscheduleJobs(triggerKeys); + } + /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void resumeTriggerGroup(String groupName) throws SchedulerException { - sched.resumeTriggerGroup(schedCtxt, groupName); + public boolean deleteJob(JobKey jobKey) + throws SchedulerException { + return sched.deleteJob(jobKey); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void resumeJob(String jobName, String groupName) - throws SchedulerException { - sched.resumeJob(schedCtxt, jobName, groupName); + public boolean unscheduleJob(TriggerKey triggerKey) + throws SchedulerException { + return sched.unscheduleJob(triggerKey); } - + /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void resumeJobGroup(String groupName) throws SchedulerException { - sched.resumeJobGroup(schedCtxt, groupName); + public Date rescheduleJob(TriggerKey triggerKey, + Trigger newTrigger) throws SchedulerException { + return sched.rescheduleJob(triggerKey, newTrigger); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void pauseAll() throws SchedulerException { - sched.pauseAll(schedCtxt); + public void triggerJob(JobKey jobKey) + throws SchedulerException { + triggerJob(jobKey, null); } - + /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void resumeAll() throws SchedulerException { - sched.resumeAll(schedCtxt); + public void triggerJob(JobKey jobKey, JobDataMap data) + throws SchedulerException { + sched.triggerJob(jobKey, data); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public String[] getJobGroupNames() throws SchedulerException { - return sched.getJobGroupNames(schedCtxt); + public void pauseTrigger(TriggerKey triggerKey) + throws SchedulerException { + sched.pauseTrigger(triggerKey); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Trigger[] getTriggersOfJob(String jobName, String groupName) - throws SchedulerException { - return sched.getTriggersOfJob(schedCtxt, jobName, groupName); + public void pauseTriggers(GroupMatcher matcher) throws SchedulerException { + sched.pauseTriggers(matcher); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public String[] getJobNames(String groupName) throws SchedulerException { - return sched.getJobNames(schedCtxt, groupName); + public void pauseJob(JobKey jobKey) + throws SchedulerException { + sched.pauseJob(jobKey); } - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

+ /** + * @see org.quartz.Scheduler#getPausedTriggerGroups() */ - public String[] getTriggerGroupNames() throws SchedulerException { - return sched.getTriggerGroupNames(schedCtxt); + public Set getPausedTriggerGroups() throws SchedulerException { + return sched.getPausedTriggerGroups(); } - + /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public String[] getTriggerNames(String groupName) throws SchedulerException { - return sched.getTriggerNames(schedCtxt, groupName); + public void pauseJobs(GroupMatcher matcher) throws SchedulerException { + sched.pauseJobs(matcher); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public JobDetail getJobDetail(String jobName, String jobGroup) - throws SchedulerException { - return sched.getJobDetail(schedCtxt, jobName, jobGroup); + public void resumeTrigger(TriggerKey triggerKey) + throws SchedulerException { + sched.resumeTrigger(triggerKey); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Trigger getTrigger(String triggerName, String triggerGroup) - throws SchedulerException { - return sched.getTrigger(schedCtxt, triggerName, triggerGroup); + public void resumeTriggers(GroupMatcher matcher) throws SchedulerException { + sched.resumeTriggers(matcher); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public int getTriggerState(String triggerName, String triggerGroup) - throws SchedulerException { - return sched.getTriggerState(schedCtxt, triggerName, triggerGroup); + public void resumeJob(JobKey jobKey) + throws SchedulerException { + sched.resumeJob(jobKey); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) - throws SchedulerException { - sched.addCalendar(schedCtxt, calName, calendar, replace, updateTriggers); + public void resumeJobs(GroupMatcher matcher) throws SchedulerException { + sched.resumeJobs(matcher); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. + * Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public boolean deleteCalendar(String calName) throws SchedulerException { - return sched.deleteCalendar(schedCtxt, calName); + public void pauseAll() throws SchedulerException { + sched.pauseAll(); } /** *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public Calendar getCalendar(String calName) throws SchedulerException { - return sched.getCalendar(schedCtxt, calName); - } - - /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler, - * passing the SchedulingContext associated with this - * instance. - *

- */ - public String[] getCalendarNames() throws SchedulerException { - return sched.getCalendarNames(schedCtxt); - } - - /////////////////////////////////////////////////////////////////////////// - /// - /// Listener-related Methods - /// - /////////////////////////////////////////////////////////////////////////// - - /** - *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void addGlobalJobListener(JobListener jobListener) { - sched.addGlobalJobListener(jobListener); + public void resumeAll() throws SchedulerException { + sched.resumeAll(); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void addJobListener(JobListener jobListener) { - sched.addJobListener(jobListener); + public List getJobGroupNames() throws SchedulerException { + return sched.getJobGroupNames(); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public boolean removeGlobalJobListener(JobListener jobListener) { - return sched.removeGlobalJobListener(jobListener); + public List getTriggersOfJob(JobKey jobKey) + throws SchedulerException { + return sched.getTriggersOfJob(jobKey); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public boolean removeJobListener(String name) { - return sched.removeJobListener(name); + public Set getJobKeys(GroupMatcher matcher) throws SchedulerException { + return sched.getJobKeys(matcher); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public List getGlobalJobListeners() { - return sched.getGlobalJobListeners(); + public List getTriggerGroupNames() throws SchedulerException { + return sched.getTriggerGroupNames(); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Set getJobListenerNames() { - return sched.getJobListenerNames(); + public Set getTriggerKeys(GroupMatcher matcher) throws SchedulerException { + return sched.getTriggerKeys(matcher); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public JobListener getJobListener(String name) { - return sched.getJobListener(name); + public JobDetail getJobDetail(JobKey jobKey) + throws SchedulerException { + return sched.getJobDetail(jobKey); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void addGlobalTriggerListener(TriggerListener triggerListener) { - sched.addGlobalTriggerListener(triggerListener); + public Trigger getTrigger(TriggerKey triggerKey) + throws SchedulerException { + return sched.getTrigger(triggerKey); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void addTriggerListener(TriggerListener triggerListener) { - sched.addTriggerListener(triggerListener); + public TriggerState getTriggerState(TriggerKey triggerKey) + throws SchedulerException { + return sched.getTriggerState(triggerKey); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public boolean removeGlobalTriggerListener(TriggerListener triggerListener) { - return sched.removeGlobalTriggerListener(triggerListener); + public void addCalendar(String calName, Calendar calendar, boolean replace, boolean updateTriggers) + throws SchedulerException { + sched.addCalendar(calName, calendar, replace, updateTriggers); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public boolean removeTriggerListener(String name) { - return sched.removeTriggerListener(name); + public boolean deleteCalendar(String calName) throws SchedulerException { + return sched.deleteCalendar(calName); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public List getGlobalTriggerListeners() { - return sched.getGlobalTriggerListeners(); + public Calendar getCalendar(String calName) throws SchedulerException { + return sched.getCalendar(calName); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public Set getTriggerListenerNames() { - return sched.getTriggerListenerNames(); + public List getCalendarNames() throws SchedulerException { + return sched.getCalendarNames(); } /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public TriggerListener getTriggerListener(String name) { - return sched.getTriggerListener(name); + public boolean checkExists(JobKey jobKey) throws SchedulerException { + return sched.checkExists(jobKey); } - + + /** *

* Calls the equivalent method on the 'proxied' QuartzScheduler. *

*/ - public void addSchedulerListener(SchedulerListener schedulerListener) { - sched.addSchedulerListener(schedulerListener); + public boolean checkExists(TriggerKey triggerKey) throws SchedulerException { + return sched.checkExists(triggerKey); } + /////////////////////////////////////////////////////////////////////////// + /// + /// Other Methods + /// + /////////////////////////////////////////////////////////////////////////// + + + /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler. - *

+ * @see org.quartz.Scheduler#setJobFactory(org.quartz.spi.JobFactory) */ - public boolean removeSchedulerListener(SchedulerListener schedulerListener) { - return sched.removeSchedulerListener(schedulerListener); + public void setJobFactory(JobFactory factory) throws SchedulerException { + sched.setJobFactory(factory); } /** - *

- * Calls the equivalent method on the 'proxied' QuartzScheduler. - *

+ * @see org.quartz.Scheduler#getListenerManager() */ - public List getSchedulerListeners() { - return sched.getSchedulerListeners(); + public ListenerManager getListenerManager() throws SchedulerException { + return sched.getListenerManager(); } - public boolean interrupt(String jobName, String groupName) throws UnableToInterruptJobException { - return sched.interrupt(schedCtxt, jobName, groupName); + public boolean interrupt(JobKey jobKey) throws UnableToInterruptJobException { + return sched.interrupt(jobKey); } - /** - * @see org.quartz.Scheduler#setJobFactory(org.quartz.spi.JobFactory) - */ - public void setJobFactory(JobFactory factory) throws SchedulerException { - sched.setJobFactory(factory); + public boolean interrupt(String fireInstanceId) throws UnableToInterruptJobException { + return sched.interrupt(fireInstanceId); } - + + } Index: 3rdParty_sources/quartz/org/quartz/impl/StdSchedulerFactory.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/StdSchedulerFactory.java (.../StdSchedulerFactory.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/StdSchedulerFactory.java (.../StdSchedulerFactory.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,23 +1,20 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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.impl; import java.beans.BeanInfo; @@ -30,14 +27,12 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; +import java.security.AccessControlException; import java.sql.SQLException; import java.util.Collection; -import java.util.Iterator; import java.util.Locale; import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.quartz.JobListener; import org.quartz.Scheduler; import org.quartz.SchedulerConfigException; @@ -47,61 +42,77 @@ import org.quartz.core.JobRunShellFactory; import org.quartz.core.QuartzScheduler; import org.quartz.core.QuartzSchedulerResources; -import org.quartz.core.SchedulingContext; +import org.quartz.ee.jta.JTAAnnotationAwareJobRunShellFactory; import org.quartz.ee.jta.JTAJobRunShellFactory; import org.quartz.ee.jta.UserTransactionHelper; import org.quartz.impl.jdbcjobstore.JobStoreSupport; +import org.quartz.impl.jdbcjobstore.Semaphore; +import org.quartz.impl.jdbcjobstore.TablePrefixAware; +import org.quartz.impl.matchers.EverythingMatcher; +import org.quartz.management.ManagementRESTServiceConfiguration; import org.quartz.simpl.RAMJobStore; import org.quartz.simpl.SimpleThreadPool; import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.InstanceIdGenerator; import org.quartz.spi.JobFactory; import org.quartz.spi.JobStore; import org.quartz.spi.SchedulerPlugin; +import org.quartz.spi.ThreadExecutor; import org.quartz.spi.ThreadPool; import org.quartz.utils.ConnectionProvider; import org.quartz.utils.DBConnectionManager; import org.quartz.utils.JNDIConnectionProvider; import org.quartz.utils.PoolingConnectionProvider; import org.quartz.utils.PropertiesParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** *

* An implementation of {@link org.quartz.SchedulerFactory} that - * does all of it's work of creating a QuartzScheduler instance - * based on the contenents of a Properties file. + * does all of its work of creating a QuartzScheduler instance + * based on the contents 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 'org.quartz.properties' to* point to the file you want. + * property 'org.quartz.properties' to point to the file you want. *

- * + * *

+ * Alternatively, you can explicitly initialize the factory by calling one of + * the initialize(xx) methods before calling getScheduler(). + *

+ * + *

* See the sample properties files that are distributed with Quartz for * information about the various settings available within the file. + * Full configuration documentation can be found at + * http://www.quartz-scheduler.org/docs/index.html *

- * + * *

- * Alternativly, you can explicitly initialize the factory by calling one of - * the initialize(xx) methods before calling getScheduler(). - *

- * - *

* Instances of the specified {@link org.quartz.spi.JobStore}, - * {@link org.quartz.spi.ThreadPool}, classes will be created + * {@link org.quartz.spi.ThreadPool}, and other SPI 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 '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 propertie's setter method. + * example if the properties file contains the property + * '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. *

* + *

+ * One property can reference another property's value by specifying a value + * following the convention of "$@other.property.name", for example, to reference + * the scheduler's instance name as the value for some other property, you + * would use "$@org.quartz.scheduler.instanceName". + *

+ * * @author James House * @author Anthony Eden * @author Mohammad Rezaei @@ -110,9 +121,9 @@ /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Constants. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -122,9 +133,26 @@ public static final String PROP_SCHED_INSTANCE_ID = "org.quartz.scheduler.instanceId"; - public static final String PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS = "org.quartz.scheduler.instanceIdGenerator.class"; - + public static final String PROP_SCHED_INSTANCE_ID_GENERATOR_PREFIX = "org.quartz.scheduler.instanceIdGenerator"; + + public static final String PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS = + PROP_SCHED_INSTANCE_ID_GENERATOR_PREFIX + ".class"; + public static final String PROP_SCHED_THREAD_NAME = "org.quartz.scheduler.threadName"; + + public static final String PROP_SCHED_SKIP_UPDATE_CHECK = "org.quartz.scheduler.skipUpdateCheck"; + + public static final String PROP_SCHED_BATCH_TIME_WINDOW = "org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow"; + + public static final String PROP_SCHED_MAX_BATCH_SIZE = "org.quartz.scheduler.batchTriggerAcquisitionMaxCount"; + + public static final String PROP_SCHED_JMX_EXPORT = "org.quartz.scheduler.jmx.export"; + + public static final String PROP_SCHED_JMX_OBJECT_NAME = "org.quartz.scheduler.jmx.objectName"; + + public static final String PROP_SCHED_JMX_PROXY = "org.quartz.scheduler.jmx.proxy"; + + public static final String PROP_SCHED_JMX_PROXY_CLASS = "org.quartz.scheduler.jmx.proxy.class"; public static final String PROP_SCHED_RMI_EXPORT = "org.quartz.scheduler.rmi.export"; @@ -138,6 +166,8 @@ public static final String PROP_SCHED_RMI_CREATE_REGISTRY = "org.quartz.scheduler.rmi.createRegistry"; + public static final String PROP_SCHED_RMI_BIND_NAME = "org.quartz.scheduler.rmi.bindName"; + public static final String PROP_SCHED_WRAP_JOB_IN_USER_TX = "org.quartz.scheduler.wrapJobExecutionInUserTransaction"; public static final String PROP_SCHED_USER_TX_URL = "org.quartz.scheduler.userTransactionURL"; @@ -146,12 +176,20 @@ public static final String PROP_SCHED_DB_FAILURE_RETRY_INTERVAL = "org.quartz.scheduler.dbFailureRetryInterval"; + public static final String PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON = "org.quartz.scheduler.makeSchedulerThreadDaemon"; + + public static final String PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD = "org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer"; + public static final String PROP_SCHED_CLASS_LOAD_HELPER_CLASS = "org.quartz.scheduler.classLoadHelper.class"; public static final String PROP_SCHED_JOB_FACTORY_CLASS = "org.quartz.scheduler.jobFactory.class"; public static final String PROP_SCHED_JOB_FACTORY_PREFIX = "org.quartz.scheduler.jobFactory"; + public static final String PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN = "org.quartz.scheduler.interruptJobsOnShutdown"; + + public static final String PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT = "org.quartz.scheduler.interruptJobsOnShutdownWithWait"; + public static final String PROP_SCHED_CONTEXT_PREFIX = "org.quartz.context.key"; public static final String PROP_THREAD_POOL_PREFIX = "org.quartz.threadPool"; @@ -160,6 +198,14 @@ public static final String PROP_JOB_STORE_PREFIX = "org.quartz.jobStore"; + public static final String PROP_JOB_STORE_LOCK_HANDLER_PREFIX = PROP_JOB_STORE_PREFIX + ".lockHandler"; + + public static final String PROP_JOB_STORE_LOCK_HANDLER_CLASS = PROP_JOB_STORE_LOCK_HANDLER_PREFIX + ".class"; + + public static final String PROP_TABLE_PREFIX = "tablePrefix"; + + public static final String PROP_SCHED_NAME = "schedName"; + public static final String PROP_JOB_STORE_CLASS = "org.quartz.jobStore.class"; public static final String PROP_JOB_STORE_USE_PROP = "org.quartz.jobStore.useProperties"; @@ -168,16 +214,40 @@ public static final String PROP_CONNECTION_PROVIDER_CLASS = "connectionProvider.class"; + /** + * @deprecated Replaced with {@link PoolingConnectionProvider#DB_DRIVER} + */ + @Deprecated public static final String PROP_DATASOURCE_DRIVER = "driver"; + /** + * @deprecated Replaced with {@link PoolingConnectionProvider#DB_URL} + */ + @Deprecated public static final String PROP_DATASOURCE_URL = "URL"; + /** + * @deprecated Replaced with {@link PoolingConnectionProvider#DB_USER} + */ + @Deprecated public static final String PROP_DATASOURCE_USER = "user"; + /** + * @deprecated Replaced with {@link PoolingConnectionProvider#DB_PASSWORD} + */ + @Deprecated public static final String PROP_DATASOURCE_PASSWORD = "password"; + /** + * @deprecated Replaced with {@link PoolingConnectionProvider#DB_MAX_CONNECTIONS} + */ + @Deprecated public static final String PROP_DATASOURCE_MAX_CONNECTIONS = "maxConnections"; + /** + * @deprecated Replaced with {@link PoolingConnectionProvider#DB_VALIDATION_QUERY} + */ + @Deprecated public static final String PROP_DATASOURCE_VALIDATION_QUERY = "validationQuery"; public static final String PROP_DATASOURCE_JNDI_URL = "jndiURL"; @@ -206,11 +276,21 @@ public static final String AUTO_GENERATE_INSTANCE_ID = "AUTO"; + public static final String PROP_THREAD_EXECUTOR = "org.quartz.threadExecutor"; + + public static final String PROP_THREAD_EXECUTOR_CLASS = "org.quartz.threadExecutor.class"; + + public static final String SYSTEM_PROPERTY_AS_INSTANCE_ID = "SYS_PROP"; + + public static final String MANAGEMENT_REST_SERVICE_ENABLED = "org.quartz.managementRESTService.enabled"; + + public static final String MANAGEMENT_REST_SERVICE_HOST_PORT = "org.quartz.managementRESTService.bind"; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Data members. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -220,45 +300,63 @@ private PropertiesParser cfg; + private final Logger log = LoggerFactory.getLogger(getClass()); + // private Scheduler scheduler; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Constructors. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + /** + * Create an uninitialized StdSchedulerFactory. + */ public StdSchedulerFactory() { } + /** + * Create a StdSchedulerFactory that has been initialized via + * {@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 static Log getLog() { - return LogFactory.getLog(StdSchedulerFactory.class); + public Logger getLog() { + return log; } /** *

* Initialize the {@link org.quartz.SchedulerFactory} with - * the contenents of a Properties file. + * 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 @@ -267,17 +365,23 @@ * you must define the system property 'org.quartz.properties' to point to * the file you want. *

- * + * *

- * System properties (envrionment variables, and -D definitions on the - * command-line when running the JVM) over-ride any properties in the - * loaded file. + * 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()}. *

*/ public void initialize() throws SchedulerException { // short-circuit if already initialized - if (cfg != null) return; - if (initException != null) throw initException; + if (cfg != null) { + return; + } + if (initException != null) { + throw initException; + } String requestedFile = System.getProperty(PROPERTIES_FILE); String propFileName = requestedFile != null ? requestedFile @@ -286,130 +390,177 @@ Properties props = new Properties(); - if (propFile.exists()) { - try { - if (requestedFile != null) propSrc = "specified file: '" - + requestedFile + "'"; - else - propSrc = "default file in current working dir: 'quartz.properties'"; + InputStream in = null; - props.load(new BufferedInputStream(new FileInputStream( - propFileName))); + try { + if (propFile.exists()) { + try { + if (requestedFile != null) { + propSrc = "specified file: '" + requestedFile + "'"; + } else { + propSrc = "default file in current working dir: 'quartz.properties'"; + } - initialize(overRideWithSysProps(props)); - } catch (IOException ioe) { - initException = new SchedulerException("Properties file: '" - + propFileName + "' could not be read.", ioe); - throw initException; - } - } else if (requestedFile != null) { - InputStream in = - Thread.currentThread().getContextClassLoader().getResourceAsStream(requestedFile); + in = new BufferedInputStream(new FileInputStream(propFileName)); + props.load(in); - if(in == null) { - initException = new SchedulerException("Properties file: '" - + requestedFile + "' could not be found."); - throw initException; - } - - propSrc = "specified file: '" + requestedFile + "' in the class resource path."; - - try { - props.load(new BufferedInputStream(in)); - initialize(overRideWithSysProps(props)); - } catch (IOException ioe) { - initException = new SchedulerException("Properties file: '" - + requestedFile + "' could not be read.", ioe); - throw initException; - } - - } else { - propSrc = "default resource file in Quartz package: 'quartz.properties'"; + } catch (IOException ioe) { + initException = new SchedulerException("Properties file: '" + + propFileName + "' could not be read.", ioe); + throw initException; + } + } else if (requestedFile != null) { + in = + Thread.currentThread().getContextClassLoader().getResourceAsStream(requestedFile); - InputStream in = getClass().getClassLoader().getResourceAsStream( - "quartz.properties"); + if(in == null) { + initException = new SchedulerException("Properties file: '" + + requestedFile + "' could not be found."); + throw initException; + } - if (in == null) - in = getClass().getClassLoader().getResourceAsStream( + propSrc = "specified file: '" + requestedFile + "' in the class resource path."; + + in = new BufferedInputStream(in); + try { + props.load(in); + } catch (IOException ioe) { + initException = new SchedulerException("Properties file: '" + + requestedFile + "' could not be read.", ioe); + throw initException; + } + + } else { + propSrc = "default resource file in Quartz package: 'quartz.properties'"; + + ClassLoader cl = getClass().getClassLoader(); + if(cl == null) + cl = findClassloader(); + if(cl == null) + throw new SchedulerConfigException("Unable to find a class loader on the current thread or class."); + + in = cl.getResourceAsStream( + "quartz.properties"); + + if (in == null) { + in = cl.getResourceAsStream( "/quartz.properties"); - if (in == null) - in = getClass().getClassLoader().getResourceAsStream( + } + if (in == null) { + in = cl.getResourceAsStream( "org/quartz/quartz.properties"); - if (in == null) { - initException = new SchedulerException( - "Default quartz.properties not found in class path"); - throw initException; + } + if (in == null) { + initException = new SchedulerException( + "Default quartz.properties not found in class path"); + throw initException; + } + try { + props.load(in); + } catch (IOException ioe) { + initException = new SchedulerException( + "Resource properties file: 'org/quartz/quartz.properties' " + + "could not be read from the classpath.", ioe); + throw initException; + } } - try { - props.load(in); - } catch (IOException ioe) { - initException = new SchedulerException( - "Resource properties file: 'org/quartz/quartz.properties' " - + "could not be read from the classpath.", ioe); - throw initException; + } finally { + if(in != null) { + try { in.close(); } catch(IOException ignore) { /* ignore */ } } - - initialize(overRideWithSysProps(props)); } + + initialize(overrideWithSysProps(props)); } - private Properties overRideWithSysProps(Properties props) { + /** + * Add all System properties to the given 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); + } - Properties sysProps = System.getProperties(); - props.putAll(sysProps); + if (sysProps != null) { + props.putAll(sysProps); + } return props; } /** *

* Initialize the {@link org.quartz.SchedulerFactory} with - * the contenents of the Properties file with the given + * the contents of the Properties file with the given * name. *

*/ public void initialize(String filename) throws SchedulerException { // short-circuit if already initialized - if (cfg != null) return; - if (initException != null) throw initException; + if (cfg != null) { + return; + } + if (initException != null) { + throw initException; + } + InputStream is = null; Properties props = new Properties(); is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename); - + try { if(is != null) { is = new BufferedInputStream(is); propSrc = "the specified file : '" + filename + "' from the class resource path."; + } else { + is = new BufferedInputStream(new FileInputStream(filename)); + propSrc = "the specified file : '" + filename + "'"; } - else { - is = new BufferedInputStream(new FileInputStream(filename)); - propSrc = "the specified file : '" + filename + "'"; - } props.load(is); } catch (IOException ioe) { initException = new SchedulerException("Properties file: '" + filename + "' could not be read.", ioe); throw initException; } + finally { + if(is != null) + try { is.close(); } catch(IOException ignore) {} + } initialize(props); } /** *

* Initialize the {@link org.quartz.SchedulerFactory} with - * the contenents of the Properties file opened with the + * the contents of the Properties file opened with the * given InputStream. *

*/ public void initialize(InputStream propertiesStream) - throws SchedulerException { + throws SchedulerException { // short-circuit if already initialized - if (cfg != null) return; - if (initException != null) throw initException; + if (cfg != null) { + return; + } + if (initException != null) { + throw initException; + } + Properties props = new Properties(); if (propertiesStream != null) { @@ -433,39 +584,42 @@ /** *

* Initialize the {@link org.quartz.SchedulerFactory} with - * the contenents of the given Properties object. + * the contents of the given Properties object. *

*/ public void initialize(Properties props) throws SchedulerException { - if (propSrc == null) - propSrc = "an externally provided properties instance."; + if (propSrc == null) { + propSrc = "an externally provided properties instance."; + } this.cfg = new PropertiesParser(props); } - /** - * - */ private Scheduler instantiate() throws SchedulerException { - if (cfg == null) initialize(); + if (cfg == null) { + initialize(); + } - if (initException != null) throw initException; + if (initException != null) { + throw initException; + } JobStore js = null; ThreadPool tp = null; QuartzScheduler qs = null; - SchedulingContext schedCtxt = null; DBConnectionManager dbMgr = null; String instanceIdGeneratorClass = null; Properties tProps = null; String userTXLocation = null; boolean wrapJobInTx = false; boolean autoId = false; long idleWaitTime = -1; - long dbFailureRetry = -1; + long dbFailureRetry = 15000L; // 15 secs String classLoadHelperClass; String jobFactoryClass; + ThreadExecutor threadExecutor; + SchedulerRepository schedRep = SchedulerRepository.getInstance(); // Get Scheduler Properties @@ -476,7 +630,7 @@ String threadName = cfg.getStringProperty(PROP_SCHED_THREAD_NAME, schedName + "_QuartzSchedulerThread"); - + String schedInstId = cfg.getStringProperty(PROP_SCHED_INSTANCE_ID, DEFAULT_INSTANCE_ID); @@ -486,11 +640,17 @@ PROP_SCHED_INSTANCE_ID_GENERATOR_CLASS, "org.quartz.simpl.SimpleInstanceIdGenerator"); } - + else if (schedInstId.equals(SYSTEM_PROPERTY_AS_INSTANCE_ID)) { + autoId = true; + instanceIdGeneratorClass = + "org.quartz.simpl.SystemPropertyInstanceIdGenerator"; + } + userTXLocation = cfg.getStringProperty(PROP_SCHED_USER_TX_URL, userTXLocation); - if (userTXLocation != null && userTXLocation.trim().length() == 0) - userTXLocation = null; + if (userTXLocation != null && userTXLocation.trim().length() == 0) { + userTXLocation = null; + } classLoadHelperClass = cfg.getStringProperty( PROP_SCHED_CLASS_LOAD_HELPER_CLASS, @@ -503,43 +663,72 @@ idleWaitTime = cfg.getLongProperty(PROP_SCHED_IDLE_WAIT_TIME, idleWaitTime); - dbFailureRetry = cfg.getLongProperty( - PROP_SCHED_DB_FAILURE_RETRY_INTERVAL, dbFailureRetry); + if(idleWaitTime > -1 && idleWaitTime < 1000) { + throw new SchedulerException("org.quartz.scheduler.idleWaitTime of less than 1000ms is not legal."); + } + + dbFailureRetry = cfg.getLongProperty(PROP_SCHED_DB_FAILURE_RETRY_INTERVAL, dbFailureRetry); + if (dbFailureRetry < 0) { + throw new SchedulerException(PROP_SCHED_DB_FAILURE_RETRY_INTERVAL + " of less than 0 ms is not legal."); + } - boolean rmiExport = cfg - .getBooleanProperty(PROP_SCHED_RMI_EXPORT, false); + boolean makeSchedulerThreadDaemon = + cfg.getBooleanProperty(PROP_SCHED_MAKE_SCHEDULER_THREAD_DAEMON); + + boolean threadsInheritInitalizersClassLoader = + cfg.getBooleanProperty(PROP_SCHED_SCHEDULER_THREADS_INHERIT_CONTEXT_CLASS_LOADER_OF_INITIALIZING_THREAD); + + boolean skipUpdateCheck = cfg.getBooleanProperty(PROP_SCHED_SKIP_UPDATE_CHECK, false); + long batchTimeWindow = cfg.getLongProperty(PROP_SCHED_BATCH_TIME_WINDOW, 0L); + int maxBatchSize = cfg.getIntProperty(PROP_SCHED_MAX_BATCH_SIZE, 1); + + boolean interruptJobsOnShutdown = cfg.getBooleanProperty(PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN, false); + boolean interruptJobsOnShutdownWithWait = cfg.getBooleanProperty(PROP_SCHED_INTERRUPT_JOBS_ON_SHUTDOWN_WITH_WAIT, false); + + boolean jmxExport = cfg.getBooleanProperty(PROP_SCHED_JMX_EXPORT); + String jmxObjectName = cfg.getStringProperty(PROP_SCHED_JMX_OBJECT_NAME); + + boolean jmxProxy = cfg.getBooleanProperty(PROP_SCHED_JMX_PROXY); + String jmxProxyClass = cfg.getStringProperty(PROP_SCHED_JMX_PROXY_CLASS); + + boolean rmiExport = cfg.getBooleanProperty(PROP_SCHED_RMI_EXPORT, false); boolean rmiProxy = cfg.getBooleanProperty(PROP_SCHED_RMI_PROXY, false); - String rmiHost = cfg - .getStringProperty(PROP_SCHED_RMI_HOST, "localhost"); + String rmiHost = cfg.getStringProperty(PROP_SCHED_RMI_HOST, "localhost"); int rmiPort = cfg.getIntProperty(PROP_SCHED_RMI_PORT, 1099); int rmiServerPort = cfg.getIntProperty(PROP_SCHED_RMI_SERVER_PORT, -1); String rmiCreateRegistry = cfg.getStringProperty( PROP_SCHED_RMI_CREATE_REGISTRY, QuartzSchedulerResources.CREATE_REGISTRY_NEVER); + String rmiBindName = cfg.getStringProperty(PROP_SCHED_RMI_BIND_NAME); + if (jmxProxy && rmiProxy) { + throw new SchedulerConfigException("Cannot proxy both RMI and JMX."); + } + + boolean managementRESTServiceEnabled = cfg.getBooleanProperty(MANAGEMENT_REST_SERVICE_ENABLED, false); + String managementRESTServiceHostAndPort = cfg.getStringProperty(MANAGEMENT_REST_SERVICE_HOST_PORT, "0.0.0.0:9889"); + Properties schedCtxtProps = cfg.getPropertyGroup(PROP_SCHED_CONTEXT_PREFIX, true); // If Proxying to remote scheduler, short-circuit here... // ~~~~~~~~~~~~~~~~~~ if (rmiProxy) { - if (autoId) + if (autoId) { schedInstId = DEFAULT_INSTANCE_ID; - - schedCtxt = new SchedulingContext(); - schedCtxt.setInstanceId(schedInstId); + } - String uid = QuartzSchedulerResources.getUniqueIdentifier( - schedName, schedInstId); + String uid = (rmiBindName == null) ? QuartzSchedulerResources.getUniqueIdentifier( + schedName, schedInstId) : rmiBindName; - RemoteScheduler remoteScheduler = new RemoteScheduler(schedCtxt, - uid, rmiHost, rmiPort); + RemoteScheduler remoteScheduler = new RemoteScheduler(uid, rmiHost, rmiPort); schedRep.bind(remoteScheduler); return remoteScheduler; } + // Create class load helper ClassLoadHelper loadHelper = null; try { @@ -551,6 +740,49 @@ + e.getMessage(), e); } loadHelper.initialize(); + + // If Proxying to remote JMX scheduler, short-circuit here... + // ~~~~~~~~~~~~~~~~~~ + if (jmxProxy) { + if (autoId) { + schedInstId = DEFAULT_INSTANCE_ID; + } + + if (jmxProxyClass == null) { + throw new SchedulerConfigException("No JMX Proxy Scheduler class provided"); + } + + RemoteMBeanScheduler jmxScheduler = null; + try { + jmxScheduler = (RemoteMBeanScheduler)loadHelper.loadClass(jmxProxyClass) + .newInstance(); + } catch (Exception e) { + throw new SchedulerConfigException( + "Unable to instantiate RemoteMBeanScheduler class.", e); + } + + if (jmxObjectName == null) { + jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId); + } + + jmxScheduler.setSchedulerObjectName(jmxObjectName); + + tProps = cfg.getPropertyGroup(PROP_SCHED_JMX_PROXY, true); + try { + setBeanProps(jmxScheduler, tProps); + } catch (Exception e) { + initException = new SchedulerException("RemoteMBeanScheduler class '" + + jmxProxyClass + "' props could not be configured.", e); + throw initException; + } + + jmxScheduler.initialize(); + + schedRep.bind(jmxScheduler); + + return jmxScheduler; + } + JobFactory jobFactory = null; if(jobFactoryClass != null) { @@ -569,12 +801,10 @@ } catch (Exception e) { initException = new SchedulerException("JobFactory class '" + jobFactoryClass + "' props could not be configured.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } - } - + } + InstanceIdGenerator instanceIdGenerator = null; if(instanceIdGeneratorClass != null) { try { @@ -585,17 +815,25 @@ "Unable to instantiate InstanceIdGenerator class: " + e.getMessage(), e); } - } - + + tProps = cfg.getPropertyGroup(PROP_SCHED_INSTANCE_ID_GENERATOR_PREFIX, true); + try { + setBeanProps(instanceIdGenerator, tProps); + } catch (Exception e) { + initException = new SchedulerException("InstanceIdGenerator class '" + + instanceIdGeneratorClass + "' props could not be configured.", e); + throw initException; + } + } + // Get ThreadPool Properties // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - String tpClass = cfg.getStringProperty(PROP_THREAD_POOL_CLASS, null); + String tpClass = cfg.getStringProperty(PROP_THREAD_POOL_CLASS, SimpleThreadPool.class.getName()); if (tpClass == null) { initException = new SchedulerException( - "ThreadPool class not specified. ", - SchedulerException.ERR_BAD_CONFIGURATION); + "ThreadPool class not specified. "); throw initException; } @@ -604,8 +842,6 @@ } catch (Exception e) { initException = new SchedulerException("ThreadPool class '" + tpClass + "' could not be instantiated.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } tProps = cfg.getPropertyGroup(PROP_THREAD_POOL_PREFIX, true); @@ -614,8 +850,6 @@ } catch (Exception e) { initException = new SchedulerException("ThreadPool class '" + tpClass + "' props could not be configured.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } @@ -627,8 +861,7 @@ if (jsClass == null) { initException = new SchedulerException( - "JobStore class not specified. ", - SchedulerException.ERR_BAD_CONFIGURATION); + "JobStore class not specified. "); throw initException; } @@ -637,28 +870,55 @@ } catch (Exception e) { initException = new SchedulerException("JobStore class '" + jsClass + "' could not be instantiated.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } - tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true); + + SchedulerDetailsSetter.setDetails(js, schedName, schedInstId); + + tProps = cfg.getPropertyGroup(PROP_JOB_STORE_PREFIX, true, new String[] {PROP_JOB_STORE_LOCK_HANDLER_PREFIX}); try { setBeanProps(js, tProps); } catch (Exception e) { initException = new SchedulerException("JobStore class '" + jsClass + "' props could not be configured.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } - if (js instanceof org.quartz.impl.jdbcjobstore.JobStoreSupport) { - ((org.quartz.impl.jdbcjobstore.JobStoreSupport) js) - .setInstanceId(schedInstId); - ((org.quartz.impl.jdbcjobstore.JobStoreSupport) js) - .setInstanceName(schedName); + if (js instanceof JobStoreSupport) { + // Install custom lock handler (Semaphore) + String lockHandlerClass = cfg.getStringProperty(PROP_JOB_STORE_LOCK_HANDLER_CLASS); + if (lockHandlerClass != null) { + try { + Semaphore lockHandler = (Semaphore)loadHelper.loadClass(lockHandlerClass).newInstance(); + + tProps = cfg.getPropertyGroup(PROP_JOB_STORE_LOCK_HANDLER_PREFIX, true); + + // If this lock handler requires the table prefix, add it to its properties. + if (lockHandler instanceof TablePrefixAware) { + tProps.setProperty( + PROP_TABLE_PREFIX, ((JobStoreSupport)js).getTablePrefix()); + tProps.setProperty( + PROP_SCHED_NAME, schedName); + } + + try { + setBeanProps(lockHandler, tProps); + } catch (Exception e) { + initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass + + "' props could not be configured.", e); + throw initException; + } + + ((JobStoreSupport)js).setLockHandler(lockHandler); + getLog().info("Using custom data access locking (synchronization): " + lockHandlerClass); + } catch (Exception e) { + initException = new SchedulerException("JobStore LockHandler class '" + lockHandlerClass + + "' could not be instantiated.", e); + throw initException; + } + } } - + // Set up any DataSources // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -677,73 +937,67 @@ } catch (Exception e) { initException = new SchedulerException("ConnectionProvider class '" + cpClass + "' could not be instantiated.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } try { // remove the class name, so it isn't attempted to be set pp.getUnderlyingProperties().remove( PROP_CONNECTION_PROVIDER_CLASS); - + setBeanProps(cp, pp.getUnderlyingProperties()); + cp.initialize(); } catch (Exception e) { initException = new SchedulerException("ConnectionProvider class '" + cpClass + "' props could not be configured.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } dbMgr = DBConnectionManager.getInstance(); dbMgr.addConnectionProvider(dsNames[i], cp); - } - else { - String dsDriver = pp - .getStringProperty(PROP_DATASOURCE_DRIVER, null); - String dsURL = pp.getStringProperty(PROP_DATASOURCE_URL, null); - boolean dsAlwaysLookup = pp.getBooleanProperty( - PROP_DATASOURCE_JNDI_ALWAYS_LOOKUP, false); - String dsUser = pp.getStringProperty(PROP_DATASOURCE_USER, ""); - String dsPass = pp.getStringProperty(PROP_DATASOURCE_PASSWORD, ""); - int dsCnt = pp.getIntProperty(PROP_DATASOURCE_MAX_CONNECTIONS, 10); - String dsJndi = pp - .getStringProperty(PROP_DATASOURCE_JNDI_URL, null); - String dsJndiInitial = pp.getStringProperty( - PROP_DATASOURCE_JNDI_INITIAL, null); - String dsJndiProvider = pp.getStringProperty( - PROP_DATASOURCE_JNDI_PROVDER, null); - String dsJndiPrincipal = pp.getStringProperty( - PROP_DATASOURCE_JNDI_PRINCIPAL, null); - String dsJndiCredentials = pp.getStringProperty( - PROP_DATASOURCE_JNDI_CREDENTIALS, null); - String dsValidation = pp.getStringProperty( - PROP_DATASOURCE_VALIDATION_QUERY, null); - + } else { + String dsJndi = pp.getStringProperty(PROP_DATASOURCE_JNDI_URL, null); + if (dsJndi != null) { + boolean dsAlwaysLookup = pp.getBooleanProperty( + PROP_DATASOURCE_JNDI_ALWAYS_LOOKUP); + String dsJndiInitial = pp.getStringProperty( + PROP_DATASOURCE_JNDI_INITIAL); + String dsJndiProvider = pp.getStringProperty( + PROP_DATASOURCE_JNDI_PROVDER); + String dsJndiPrincipal = pp.getStringProperty( + PROP_DATASOURCE_JNDI_PRINCIPAL); + String dsJndiCredentials = pp.getStringProperty( + PROP_DATASOURCE_JNDI_CREDENTIALS); Properties props = null; if (null != dsJndiInitial || null != dsJndiProvider || null != dsJndiPrincipal || null != dsJndiCredentials) { props = new Properties(); - if (dsJndiInitial != null) - props.put(PROP_DATASOURCE_JNDI_INITIAL, - dsJndiInitial); - if (dsJndiProvider != null) - props.put(PROP_DATASOURCE_JNDI_PROVDER, - dsJndiProvider); - if (dsJndiPrincipal != null) - props.put(PROP_DATASOURCE_JNDI_PRINCIPAL, - dsJndiPrincipal); - if (dsJndiCredentials != null) - props.put(PROP_DATASOURCE_JNDI_CREDENTIALS, - dsJndiCredentials); + if (dsJndiInitial != null) { + props.put(PROP_DATASOURCE_JNDI_INITIAL, + dsJndiInitial); + } + if (dsJndiProvider != null) { + props.put(PROP_DATASOURCE_JNDI_PROVDER, + dsJndiProvider); + } + if (dsJndiPrincipal != null) { + props.put(PROP_DATASOURCE_JNDI_PRINCIPAL, + dsJndiPrincipal); + } + if (dsJndiCredentials != null) { + props.put(PROP_DATASOURCE_JNDI_CREDENTIALS, + dsJndiCredentials); + } } JNDIConnectionProvider cp = new JNDIConnectionProvider(dsJndi, props, dsAlwaysLookup); dbMgr = DBConnectionManager.getInstance(); dbMgr.addConnectionProvider(dsNames[i], cp); } else { + String dsDriver = pp.getStringProperty(PoolingConnectionProvider.DB_DRIVER); + String dsURL = pp.getStringProperty(PoolingConnectionProvider.DB_URL); + if (dsDriver == null) { initException = new SchedulerException( "Driver not specified for DataSource: " @@ -757,9 +1011,7 @@ throw initException; } try { - PoolingConnectionProvider cp = new PoolingConnectionProvider( - dsDriver, dsURL, dsUser, dsPass, dsCnt, - dsValidation); + PoolingConnectionProvider cp = new PoolingConnectionProvider(pp.getUnderlyingProperties()); dbMgr = DBConnectionManager.getInstance(); dbMgr.addConnectionProvider(dsNames[i], cp); } catch (SQLException sqle) { @@ -769,9 +1021,9 @@ throw initException; } } - + } - + } // Set up any SchedulerPlugins @@ -788,8 +1040,7 @@ if (plugInClass == null) { initException = new SchedulerException( "SchedulerPlugin class not specified for plugin '" - + pluginNames[i] + "'", - SchedulerException.ERR_BAD_CONFIGURATION); + + pluginNames[i] + "'"); throw initException; } SchedulerPlugin plugin = null; @@ -800,8 +1051,6 @@ initException = new SchedulerException( "SchedulerPlugin class '" + plugInClass + "' could not be instantiated.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } try { @@ -810,60 +1059,61 @@ initException = new SchedulerException( "JobStore SchedulerPlugin '" + plugInClass + "' props could not be configured.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } + plugins[i] = plugin; } // Set up any JobListeners // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Class[] strArg = new Class[] { String.class }; + Class[] strArg = new Class[] { String.class }; String[] jobListenerNames = cfg.getPropertyGroups(PROP_JOB_LISTENER_PREFIX); JobListener[] jobListeners = new JobListener[jobListenerNames.length]; for (int i = 0; i < jobListenerNames.length; i++) { Properties lp = cfg.getPropertyGroup(PROP_JOB_LISTENER_PREFIX + "." + jobListenerNames[i], true); - + String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null); if (listenerClass == null) { initException = new SchedulerException( "JobListener class not specified for listener '" - + jobListenerNames[i] + "'", - SchedulerException.ERR_BAD_CONFIGURATION); + + jobListenerNames[i] + "'"); throw initException; } JobListener listener = null; try { - listener = (JobListener) + listener = (JobListener) loadHelper.loadClass(listenerClass).newInstance(); } catch (Exception e) { initException = new SchedulerException( "JobListener class '" + listenerClass + "' could not be instantiated.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } try { - Method nameSetter = listener.getClass().getMethod("setName", strArg); - if(nameSetter != null) + Method nameSetter = null; + try { + nameSetter = listener.getClass().getMethod("setName", strArg); + } + catch(NoSuchMethodException ignore) { + /* do nothing */ + } + if(nameSetter != null) { nameSetter.invoke(listener, new Object[] {jobListenerNames[i] } ); + } setBeanProps(listener, lp); } catch (Exception e) { initException = new SchedulerException( "JobListener '" + listenerClass + "' props could not be configured.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } jobListeners[i] = listener; } - + // Set up any TriggerListeners // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -872,172 +1122,270 @@ for (int i = 0; i < triggerListenerNames.length; i++) { Properties lp = cfg.getPropertyGroup(PROP_TRIGGER_LISTENER_PREFIX + "." + triggerListenerNames[i], true); - + String listenerClass = lp.getProperty(PROP_LISTENER_CLASS, null); if (listenerClass == null) { initException = new SchedulerException( "TriggerListener class not specified for listener '" - + triggerListenerNames[i] + "'", - SchedulerException.ERR_BAD_CONFIGURATION); + + triggerListenerNames[i] + "'"); throw initException; } TriggerListener listener = null; try { - listener = (TriggerListener) + listener = (TriggerListener) loadHelper.loadClass(listenerClass).newInstance(); } catch (Exception e) { initException = new SchedulerException( "TriggerListener class '" + listenerClass + "' could not be instantiated.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } try { - Method nameSetter = listener.getClass().getMethod("setName", strArg); - if(nameSetter != null) + Method nameSetter = null; + try { + nameSetter = listener.getClass().getMethod("setName", strArg); + } + catch(NoSuchMethodException ignore) { /* do nothing */ } + if(nameSetter != null) { nameSetter.invoke(listener, new Object[] {triggerListenerNames[i] } ); + } setBeanProps(listener, lp); } catch (Exception e) { initException = new SchedulerException( "TriggerListener '" + listenerClass + "' props could not be configured.", e); - initException - .setErrorCode(SchedulerException.ERR_BAD_CONFIGURATION); throw initException; } triggerListeners[i] = listener; } - - - // Fire everything up - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - JobRunShellFactory jrsf = null; // Create correct run-shell factory... - UserTransactionHelper userTxHelper = null; + boolean tpInited = false; + boolean qsInited = false; - if (wrapJobInTx) - userTxHelper = new UserTransactionHelper(userTXLocation); - if (wrapJobInTx) jrsf = new JTAJobRunShellFactory(userTxHelper); - else - jrsf = new StdJobRunShellFactory(); + // Get ThreadExecutor Properties + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - if (autoId) { + String threadExecutorClass = cfg.getStringProperty(PROP_THREAD_EXECUTOR_CLASS); + if (threadExecutorClass != null) { + tProps = cfg.getPropertyGroup(PROP_THREAD_EXECUTOR, true); try { - schedInstId = DEFAULT_INSTANCE_ID; - if (js instanceof org.quartz.impl.jdbcjobstore.JobStoreSupport) { - if(((org.quartz.impl.jdbcjobstore.JobStoreSupport) js) - .isClustered()) { - schedInstId = instanceIdGenerator.generateInstanceId(); - } - } + threadExecutor = (ThreadExecutor) loadHelper.loadClass(threadExecutorClass).newInstance(); + log.info("Using custom implementation for ThreadExecutor: " + threadExecutorClass); + + setBeanProps(threadExecutor, tProps); } catch (Exception e) { - getLog().error("Couldn't generate instance Id!", e); - throw new IllegalStateException( - "Cannot run without an instance id."); + initException = new SchedulerException( + "ThreadExecutor class '" + threadExecutorClass + "' could not be instantiated.", e); + throw initException; } + } else { + log.info("Using default implementation for ThreadExecutor"); + threadExecutor = new DefaultThreadExecutor(); } - if (js instanceof JobStoreSupport) { - JobStoreSupport jjs = (JobStoreSupport) js; - jjs.setInstanceId(schedInstId); - jjs.setDbRetryInterval(dbFailureRetry); - } - - QuartzSchedulerResources rsrcs = new QuartzSchedulerResources(); - rsrcs.setName(schedName); - rsrcs.setThreadName(threadName); - rsrcs.setInstanceId(schedInstId); - rsrcs.setJobRunShellFactory(jrsf); - if (rmiExport) { - rsrcs.setRMIRegistryHost(rmiHost); - rsrcs.setRMIRegistryPort(rmiPort); - rsrcs.setRMIServerPort(rmiServerPort); - rsrcs.setRMICreateRegistryStrategy(rmiCreateRegistry); - } - rsrcs.setThreadPool(tp); - if(tp instanceof SimpleThreadPool) - ((SimpleThreadPool)tp).setThreadNamePrefix(schedName + "_Worker"); - tp.initialize(); - - rsrcs.setJobStore(js); + // Fire everything up + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + try { + + + JobRunShellFactory jrsf = null; // Create correct run-shell factory... + + if (userTXLocation != null) { + UserTransactionHelper.setUserTxLocation(userTXLocation); + } + + if (wrapJobInTx) { + jrsf = new JTAJobRunShellFactory(); + } else { + jrsf = new JTAAnnotationAwareJobRunShellFactory(); + } + + if (autoId) { + try { + schedInstId = DEFAULT_INSTANCE_ID; + if (js.isClustered()) { + schedInstId = instanceIdGenerator.generateInstanceId(); + } + } catch (Exception e) { + getLog().error("Couldn't generate instance Id!", e); + throw new IllegalStateException("Cannot run without an instance id."); + } + } - schedCtxt = new SchedulingContext(); - schedCtxt.setInstanceId(rsrcs.getInstanceId()); + if (js.getClass().getName().startsWith("org.terracotta.quartz")) { + try { + String uuid = (String) js.getClass().getMethod("getUUID").invoke(js); + if(schedInstId.equals(DEFAULT_INSTANCE_ID)) { + schedInstId = "TERRACOTTA_CLUSTERED,node=" + uuid; + if (jmxObjectName == null) { + jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId); + } + } else if(jmxObjectName == null) { + jmxObjectName = QuartzSchedulerResources.generateJMXObjectName(schedName, schedInstId + ",node=" + uuid); + } + } catch(Exception e) { + throw new RuntimeException("Problem obtaining node id from TerracottaJobStore.", e); + } - qs = new QuartzScheduler(rsrcs, schedCtxt, idleWaitTime, dbFailureRetry); + if(null == cfg.getStringProperty(PROP_SCHED_JMX_EXPORT)) { + jmxExport = true; + } + } + + if (js instanceof JobStoreSupport) { + JobStoreSupport jjs = (JobStoreSupport)js; + jjs.setDbRetryInterval(dbFailureRetry); + if(threadsInheritInitalizersClassLoader) + jjs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader); + + jjs.setThreadExecutor(threadExecutor); + } + + QuartzSchedulerResources rsrcs = new QuartzSchedulerResources(); + rsrcs.setName(schedName); + rsrcs.setThreadName(threadName); + rsrcs.setInstanceId(schedInstId); + rsrcs.setJobRunShellFactory(jrsf); + rsrcs.setMakeSchedulerThreadDaemon(makeSchedulerThreadDaemon); + rsrcs.setThreadsInheritInitializersClassLoadContext(threadsInheritInitalizersClassLoader); + rsrcs.setRunUpdateCheck(!skipUpdateCheck); + rsrcs.setBatchTimeWindow(batchTimeWindow); + rsrcs.setMaxBatchSize(maxBatchSize); + rsrcs.setInterruptJobsOnShutdown(interruptJobsOnShutdown); + rsrcs.setInterruptJobsOnShutdownWithWait(interruptJobsOnShutdownWithWait); + rsrcs.setJMXExport(jmxExport); + rsrcs.setJMXObjectName(jmxObjectName); - // if(usingJSCMT) - // qs.setSignalOnSchedulingChange(false); // TODO: fixed? (don't need - // this any more?) + if (managementRESTServiceEnabled) { + ManagementRESTServiceConfiguration managementRESTServiceConfiguration = new ManagementRESTServiceConfiguration(); + managementRESTServiceConfiguration.setBind(managementRESTServiceHostAndPort); + managementRESTServiceConfiguration.setEnabled(managementRESTServiceEnabled); + rsrcs.setManagementRESTServiceConfiguration(managementRESTServiceConfiguration); + } + + if (rmiExport) { + rsrcs.setRMIRegistryHost(rmiHost); + rsrcs.setRMIRegistryPort(rmiPort); + rsrcs.setRMIServerPort(rmiServerPort); + rsrcs.setRMICreateRegistryStrategy(rmiCreateRegistry); + rsrcs.setRMIBindName(rmiBindName); + } + + SchedulerDetailsSetter.setDetails(tp, schedName, schedInstId); - // Create Scheduler ref... - Scheduler scheduler = instantiate(rsrcs, qs); - - // set job factory if specified - if(jobFactory != null) - qs.setJobFactory(jobFactory); + rsrcs.setThreadExecutor(threadExecutor); + threadExecutor.initialize(); - // add plugins - for (int i = 0; i < plugins.length; i++) { - plugins[i].initialize(pluginNames[i], scheduler); - qs.addSchedulerPlugin(plugins[i]); + rsrcs.setThreadPool(tp); + if(tp instanceof SimpleThreadPool) { + if(threadsInheritInitalizersClassLoader) + ((SimpleThreadPool)tp).setThreadsInheritContextClassLoaderOfInitializingThread(threadsInheritInitalizersClassLoader); + } + tp.initialize(); + tpInited = true; + + rsrcs.setJobStore(js); + + // add plugins + for (int i = 0; i < plugins.length; i++) { + rsrcs.addSchedulerPlugin(plugins[i]); + } + + qs = new QuartzScheduler(rsrcs, idleWaitTime, dbFailureRetry); + qsInited = true; + + // Create Scheduler ref... + Scheduler scheduler = instantiate(rsrcs, qs); + + // set job factory if specified + if(jobFactory != null) { + qs.setJobFactory(jobFactory); + } + + // Initialize plugins now that we have a Scheduler instance. + for (int i = 0; i < plugins.length; i++) { + plugins[i].initialize(pluginNames[i], scheduler, loadHelper); + } + + // add listeners + for (int i = 0; i < jobListeners.length; i++) { + qs.getListenerManager().addJobListener(jobListeners[i], EverythingMatcher.allJobs()); + } + for (int i = 0; i < triggerListeners.length; i++) { + qs.getListenerManager().addTriggerListener(triggerListeners[i], EverythingMatcher.allTriggers()); + } + + // set scheduler context data... + for(Object key: schedCtxtProps.keySet()) { + String val = schedCtxtProps.getProperty((String) key); + scheduler.getContext().put((String)key, val); + } + + // fire up job store, and runshell factory + + js.setInstanceId(schedInstId); + js.setInstanceName(schedName); + js.setThreadPoolSize(tp.getPoolSize()); + js.initialize(loadHelper, qs.getSchedulerSignaler()); + + jrsf.initialize(scheduler); + + qs.initialize(); + + getLog().info( + "Quartz scheduler '" + scheduler.getSchedulerName() + + "' initialized from " + propSrc); + + getLog().info("Quartz scheduler version: " + qs.getVersion()); + + // prevents the repository from being garbage collected + qs.addNoGCObject(schedRep); + // prevents the db manager from being garbage collected + if (dbMgr != null) { + qs.addNoGCObject(dbMgr); + } + + schedRep.bind(scheduler); + return scheduler; } - - // add listeners - for (int i = 0; i < jobListeners.length; i++) { - qs.addGlobalJobListener(jobListeners[i]); + catch(SchedulerException e) { + shutdownFromInstantiateException(tp, qs, tpInited, qsInited); + throw e; } - for (int i = 0; i < triggerListeners.length; i++) { - qs.addGlobalTriggerListener(triggerListeners[i]); + catch(RuntimeException re) { + shutdownFromInstantiateException(tp, qs, tpInited, qsInited); + throw re; } - - // set scheduler context data... - Iterator itr = schedCtxtProps.keySet().iterator(); - while(itr.hasNext()) { - String key = (String) itr.next(); - String val = schedCtxtProps.getProperty(key); - - scheduler.getContext().put(key, val); + catch(Error re) { + shutdownFromInstantiateException(tp, qs, tpInited, qsInited); + throw re; } - - // fire up job store, and runshell factory + } - js.initialize(loadHelper, qs.getSchedulerSignaler()); - - jrsf.initialize(scheduler, schedCtxt); - - getLog().info( - "Quartz scheduler '" + scheduler.getSchedulerName() - + "' initialized from " + propSrc); - - getLog().info("Quartz scheduler version: " + qs.getVersion()); - - // prevents the repository from being garbage collected - qs.addNoGCObject(schedRep); - // prevents the db manager from being garbage collected - if (dbMgr != null) qs.addNoGCObject(dbMgr); - - schedRep.bind(scheduler); - - return scheduler; + private void shutdownFromInstantiateException(ThreadPool tp, QuartzScheduler qs, boolean tpInited, boolean qsInited) { + try { + if(qsInited) + qs.shutdown(false); + else if(tpInited) + tp.shutdown(false); + } catch (Exception e) { + getLog().error("Got another exception while shutting down after instantiation exception", e); + } } protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) { - SchedulingContext schedCtxt = new SchedulingContext(); - schedCtxt.setInstanceId(rsrcs.getInstanceId()); - - Scheduler scheduler = new StdScheduler(qs, schedCtxt); + + Scheduler scheduler = new StdScheduler(qs); return scheduler; } - + private void setBeanProps(Object obj, Properties props) - throws NoSuchMethodException, IllegalAccessException, + throws NoSuchMethodException, IllegalAccessException, java.lang.reflect.InvocationTargetException, IntrospectionException, SchedulerConfigException { props.remove("class"); @@ -1046,7 +1394,7 @@ PropertyDescriptor[] propDescs = bi.getPropertyDescriptors(); PropertiesParser pp = new PropertiesParser(props); - java.util.Enumeration keys = props.keys(); + java.util.Enumeration keys = props.keys(); while (keys.hasMoreElements()) { String name = (String) keys.nextElement(); String c = name.substring(0, 1).toUpperCase(Locale.US); @@ -1055,38 +1403,44 @@ java.lang.reflect.Method setMeth = getSetMethod(methName, propDescs); try { - if (setMeth == null) - throw new NoSuchMethodException( - "No setter for property '" + name + "'"); + if (setMeth == null) { + throw new NoSuchMethodException( + "No setter for property '" + name + "'"); + } - Class[] params = setMeth.getParameterTypes(); - if (params.length != 1) - throw new NoSuchMethodException( - "No 1-argument setter for property '" + name - + "'"); - + Class[] params = setMeth.getParameterTypes(); + if (params.length != 1) { + throw new NoSuchMethodException( + "No 1-argument setter for property '" + name + "'"); + } + + // does the property value reference another property's value? If so, swap to look at its value + PropertiesParser refProps = pp; + String refName = pp.getStringProperty(name); + if(refName != null && refName.startsWith("$@")) { + refName = refName.substring(2); + refProps = cfg; + } + else + refName = name; + if (params[0].equals(int.class)) { - setMeth.invoke(obj, new Object[]{new Integer(pp - .getIntProperty(name))}); + setMeth.invoke(obj, new Object[]{Integer.valueOf(refProps.getIntProperty(refName))}); } else if (params[0].equals(long.class)) { - setMeth.invoke(obj, new Object[]{new Long(pp - .getLongProperty(name))}); + setMeth.invoke(obj, new Object[]{Long.valueOf(refProps.getLongProperty(refName))}); } else if (params[0].equals(float.class)) { - setMeth.invoke(obj, new Object[]{new Float(pp - .getFloatProperty(name))}); + setMeth.invoke(obj, new Object[]{Float.valueOf(refProps.getFloatProperty(refName))}); } else if (params[0].equals(double.class)) { - setMeth.invoke(obj, new Object[]{new Double(pp - .getDoubleProperty(name))}); + setMeth.invoke(obj, new Object[]{Double.valueOf(refProps.getDoubleProperty(refName))}); } else if (params[0].equals(boolean.class)) { - setMeth.invoke(obj, new Object[]{new Boolean(pp - .getBooleanProperty(name))}); + setMeth.invoke(obj, new Object[]{Boolean.valueOf(refProps.getBooleanProperty(refName))}); } else if (params[0].equals(String.class)) { - setMeth.invoke(obj, - new Object[]{pp.getStringProperty(name)}); - } else + setMeth.invoke(obj, new Object[]{refProps.getStringProperty(refName)}); + } else { throw new NoSuchMethodException( "No primitive-type setter for property '" + name + "'"); + } } catch (NumberFormatException nfe) { throw new SchedulerConfigException("Could not parse property '" + name + "' into correct data type: " + nfe.toString()); @@ -1099,54 +1453,67 @@ for (int i = 0; i < props.length; i++) { java.lang.reflect.Method wMeth = props[i].getWriteMethod(); - if (wMeth != null && wMeth.getName().equals(name)) return wMeth; + if (wMeth != null && wMeth.getName().equals(name)) { + return wMeth; + } } return null; } - private Class loadClass(String className) throws ClassNotFoundException { + private Class loadClass(String className) throws ClassNotFoundException, SchedulerConfigException { try { - return Thread.currentThread().getContextClassLoader().loadClass( - className); + ClassLoader cl = findClassloader(); + if(cl != null) + return cl.loadClass(className); + throw new SchedulerConfigException("Unable to find a class loader on the current thread or class."); } catch (ClassNotFoundException e) { - return getClass().getClassLoader().loadClass(className); + if(getClass().getClassLoader() != null) + return getClass().getClassLoader().loadClass(className); + throw e; } } - + + private ClassLoader findClassloader() { + // work-around set context loader for windows-service started jvms (QUARTZ-748) + if(Thread.currentThread().getContextClassLoader() == null && getClass().getClassLoader() != null) { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + } + return Thread.currentThread().getContextClassLoader(); + } + private String getSchedulerName() { return cfg.getStringProperty(PROP_SCHED_INSTANCE_NAME, "QuartzScheduler"); } - private String getSchedulerInstId() { - return cfg.getStringProperty(PROP_SCHED_INSTANCE_ID, - DEFAULT_INSTANCE_ID); - } - /** *

* 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. *

*/ public Scheduler getScheduler() throws SchedulerException { - if (cfg == null) initialize(); + if (cfg == null) { + initialize(); + } SchedulerRepository schedRep = SchedulerRepository.getInstance(); Scheduler sched = schedRep.lookup(getSchedulerName()); if (sched != null) { - if (sched.isShutdown()) schedRep.remove(getSchedulerName()); - else + if (sched.isShutdown()) { + schedRep.remove(getSchedulerName()); + } else { return sched; + } } sched = instantiate(); @@ -1159,7 +1526,7 @@ * Returns a handle to the default Scheduler, creating it if it does not * yet exist. *

- * + * * @see #initialize() */ public static Scheduler getDefaultScheduler() throws SchedulerException { @@ -1184,8 +1551,7 @@ * StdSchedulerFactory instance.). *

*/ - public Collection getAllSchedulers() throws SchedulerException { + public Collection getAllSchedulers() throws SchedulerException { return SchedulerRepository.getInstance().lookupAll(); } - } Index: 3rdParty_sources/quartz/org/quartz/impl/calendar/AnnualCalendar.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/calendar/AnnualCalendar.java (.../AnnualCalendar.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/calendar/AnnualCalendar.java (.../AnnualCalendar.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,18 +15,14 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - * and Juergen Donnerstag (c) 2002, EDS 2002 - */ - package 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 org.quartz.Calendar; @@ -44,35 +40,41 @@ public class AnnualCalendar extends BaseCalendar implements Calendar, Serializable { - private ArrayList excludeDays = new ArrayList(); + static final long serialVersionUID = 7346867105876610961L; + private ArrayList excludeDays = new ArrayList(); + // true, if excludeDays is sorted private boolean dataSorted = false; - /** - *

- * Constructor - *

- */ public AnnualCalendar() { - super(); } - /** - *

- * Constructor - *

- */ public AnnualCalendar(Calendar baseCalendar) { super(baseCalendar); } + public AnnualCalendar(TimeZone timeZone) { + super(timeZone); + } + + public AnnualCalendar(Calendar baseCalendar, TimeZone timeZone) { + super(baseCalendar, timeZone); + } + + @Override + public Object clone() { + AnnualCalendar clone = (AnnualCalendar) super.clone(); + clone.excludeDays = new ArrayList(excludeDays); + return clone; + } + /** *

* Get the array which defines the exclude-value of each day of month *

*/ - public ArrayList getDaysExcluded() { + public ArrayList getDaysExcluded() { return excludeDays; } @@ -82,10 +84,17 @@ *

*/ public boolean isDayExcluded(java.util.Calendar day) { - if (day == null) - throw new IllegalArgumentException( - "Parameter day must not be null"); + 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); @@ -94,16 +103,22 @@ dataSorted = true; } - Iterator iter = excludeDays.iterator(); + 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 (dmonth < cl.get(java.util.Calendar.MONTH)) { + return false; + } - if (dday != cl.get(java.util.Calendar.DAY_OF_MONTH)) continue; + if (dday != cl.get(java.util.Calendar.DAY_OF_MONTH)) { + continue; + } - if (dmonth != cl.get(java.util.Calendar.MONTH)) continue; + if (dmonth != cl.get(java.util.Calendar.MONTH)) { + continue; + } return true; } @@ -113,14 +128,17 @@ /** *

- * Redefine the array of days excluded. The array must of size greater or - * equal 31. + * Redefine the list of days excluded. The ArrayList + * should contain java.util.Calendar objects. *

*/ - public void setDaysExcluded(ArrayList days) { - if (days == null) excludeDays = new ArrayList(); + public void setDaysExcluded(ArrayList days) { + if (days == null) { + excludeDays = new ArrayList(); + } else { + excludeDays = days; + } - excludeDays = days; dataSorted = false; } @@ -130,13 +148,68 @@ *

*/ public void setDayExcluded(java.util.Calendar day, boolean exclude) { - if (isDayExcluded(day)) return; + if (exclude) { + if (isDayExcluded(day)) { + return; + } - excludeDays.add(day); - dataSorted = false; + 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 the day to exclude + */ + 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. @@ -146,12 +219,13 @@ * Note that this Calendar is only has full-day precision. *

*/ + @Override 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 = getJavaCalendar(timeStamp); + java.util.Calendar day = createJavaCalendar(timeStamp); return !(isDayExcluded(day)); } @@ -167,18 +241,20 @@ * Note that this Calendar is only has full-day precision. *

*/ + @Override public long getNextIncludedTime(long timeStamp) { // Call base calendar implementation first long baseTime = super.getNextIncludedTime(timeStamp); - if ((baseTime > 0) && (baseTime > timeStamp)) timeStamp = baseTime; + if ((baseTime > 0) && (baseTime > timeStamp)) { + timeStamp = baseTime; + } // Get timestamp for 00:00:00 - long newTimeStamp = buildHoliday(timeStamp); + java.util.Calendar day = getStartOfDayJavaCalendar(timeStamp); + if (isDayExcluded(day) == false) { + return timeStamp; // return the original value + } - java.util.Calendar day = getJavaCalendar(newTimeStamp); - if (isDayExcluded(day) == false) return timeStamp; // return the - // original value - while (isDayExcluded(day) == true) { day.add(java.util.Calendar.DATE, 1); } @@ -187,25 +263,34 @@ } } -class CalendarComparator implements Comparator -{ +class CalendarComparator implements Comparator, Serializable { + + private static final long serialVersionUID = 7346867105876610961L; 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; + + public int compare(java.util.Calendar c1, java.util.Calendar c2) { - if(c1.before(c2)) + 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; - else if(c1.after(c2)) + } + if (month1 > month2) { + return 1; + } + if (day1 < day2) { + return -1; + } + if (day1 > day2) { return 1; - else - return 0; - } + } + return 0; + } } Index: 3rdParty_sources/quartz/org/quartz/impl/calendar/BaseCalendar.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/calendar/BaseCalendar.java (.../BaseCalendar.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/calendar/BaseCalendar.java (.../BaseCalendar.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,29 +1,25 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.calendar; import java.io.Serializable; import java.util.Date; +import java.util.TimeZone; import org.quartz.Calendar; @@ -33,44 +29,70 @@ * 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 org.quartz.Calendar - * + * * @author Juergen Donnerstag * @author James House */ -public class BaseCalendar implements Calendar, Serializable { +public class BaseCalendar implements Calendar, Serializable, Cloneable { + 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); + } + /** - *

- * Default Constructor - *

+ * @param timeZone The time zone to use for this Calendar, null + * if {@link TimeZone#getDefault()} should be used */ - public BaseCalendar() { + public BaseCalendar(TimeZone timeZone) { + setTimeZone(timeZone); } /** - *

- * Constructor - *

+ * @param timeZone The time zone to use for this Calendar, null + * if {@link TimeZone#getDefault()} should be used */ - public BaseCalendar(Calendar baseCalendar) { + public BaseCalendar(Calendar baseCalendar, TimeZone timeZone) { setBaseCalendar(baseCalendar); + setTimeZone(timeZone); } + @Override + public Object clone() { + try { + BaseCalendar clone = (BaseCalendar) super.clone(); + if (getBaseCalendar() != null) { + clone.baseCalendar = (Calendar) getBaseCalendar().clone(); + } + if(getTimeZone() != null) + clone.timeZone = (TimeZone) getTimeZone().clone(); + return clone; + } catch (CloneNotSupportedException ex) { + throw new IncompatibleClassChangeError("Not Cloneable."); + } + } + /** *

* Set a new base calendar or remove the existing one @@ -94,7 +116,7 @@ * Return the description given to the Calendar instance by * its creator (if any). *

- * + * * @return null if no description was set. */ public String getDescription() { @@ -113,19 +135,41 @@ } /** + * Returns the time zone for which this 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 org.quartz.Calendar#isTimeIncluded(long) */ public boolean isTimeIncluded(long timeStamp) { - if (timeStamp <= 0) - throw new IllegalArgumentException( - "timeStamp must be greater 0"); + if (timeStamp <= 0) { + throw new IllegalArgumentException( + "timeStamp must be greater 0"); + } if (baseCalendar != null) { if (baseCalendar.isTimeIncluded(timeStamp) == false) { return false; } @@ -140,62 +184,81 @@ * Calendar after the given time. Return the original value if timeStamp is * included. Return 0 if all days are excluded. *

- * + * * @see org.quartz.Calendar#getNextIncludedTime(long) */ public long getNextIncludedTime(long timeStamp) { - if (timeStamp <= 0) - throw new IllegalArgumentException( - "timeStamp must be greater 0"); + if (timeStamp <= 0) { + throw new IllegalArgumentException( + "timeStamp must be greater 0"); + } - if (baseCalendar != null) { return baseCalendar - .getNextIncludedTime(timeStamp); } + 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. - *

+ * Build a {@link java.util.Calendar} for the given timeStamp. + * The new Calendar will use the BaseCalendar time zone if it + * is not null. */ - static public Date buildHoliday(Date excludedDate) { - java.util.Calendar cl = java.util.Calendar.getInstance(); - java.util.Calendar clEx = java.util.Calendar.getInstance(); - clEx.setTime(excludedDate); + protected java.util.Calendar createJavaCalendar(long timeStamp) { + java.util.Calendar calendar = createJavaCalendar(); + calendar.setTime(new Date(timeStamp)); + return calendar; + } - cl.setLenient(false); - cl.clear(); - cl.set(clEx.get(java.util.Calendar.YEAR), clEx - .get(java.util.Calendar.MONTH), clEx - .get(java.util.Calendar.DATE)); - - return cl.getTime(); + /** + * 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()); } /** - *

- * Utility method. Return just the date of excludeDate. The time fraction - * will be reset to 00.00:00. - *

+ * 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. */ - static public long buildHoliday(long timeStamp) { - return buildHoliday(new Date(timeStamp)).getTime(); + 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; } /** - *

- * Utility method. Return a java.util.Calendar for timeStamp. - *

- * - * @param timeStamp - * @return Calendar + * 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. */ - static public java.util.Calendar getJavaCalendar(long timeStamp) { - java.util.Calendar cl = java.util.Calendar.getInstance(); - cl.setTime(new Date(timeStamp)); - return cl; + 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; } } Index: 3rdParty_sources/quartz/org/quartz/impl/calendar/CronCalendar.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/calendar/CronCalendar.java (.../CronCalendar.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/calendar/CronCalendar.java (.../CronCalendar.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -2,14 +2,16 @@ import java.text.ParseException; import java.util.Date; +import java.util.TimeZone; +import org.quartz.Calendar; import org.quartz.CronExpression; /** * This implementation of the Calendar excludes the set of times expressed by a * given {@link 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-24 ? * *". + * 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 @@ -20,66 +22,107 @@ * trigger includes, and they will cancel each other out. * * @author Aaron Craven - * @version $Revision$ $Date$ */ public class CronCalendar extends BaseCalendar { static final long serialVersionUID = -8172103999750856831L; - - private String name; + CronExpression cronExpression; /** * Create a CronCalendar with the given cron expression and no * baseCalendar. * - * @param name the name for the DailyCalendar * @param expression a String representation of the desired cron expression */ - public CronCalendar(String name, String expression) - throws ParseException { - super(); - this.name = name; - this.cronExpression = new CronExpression(expression); + public CronCalendar(String expression) + throws ParseException { + this(null, expression, null); } /** - * Create a CronCalendar with the given cron exprssion and + * Create a CronCalendar with the given cron expression and * baseCalendar. * - * @param name the name for the DailyCalendar * @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(String name, org.quartz.Calendar baseCalendar, - String expression) throws ParseException { + 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.name = name; this.cronExpression = new CronExpression(expression); + this.cronExpression.setTimeZone(timeZone); } + + @Override + public Object clone() { + CronCalendar clone = (CronCalendar) super.clone(); + clone.cronExpression = new CronExpression(cronExpression); + return clone; + } /** - * Returns the name of the CronCalendar - * - * @return the name of the CronCalendar + * Returns the time zone for which the CronExpression of + * this CronCalendar will be resolved. + *

+ * Overrides {@link BaseCalendar#getTimeZone()} to + * defer to its CronExpression. + *

*/ - public String getName() { - return name; + @Override + public TimeZone getTimeZone() { + return cronExpression.getTimeZone(); } /** + * Sets the time zone for which the 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. + *

+ */ + @Override + public void setTimeZone(TimeZone timeZone) { + cronExpression.setTimeZone(timeZone); + } + + /** * 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 */ + @Override public boolean isTimeIncluded(long timeInMillis) { if ((getBaseCalendar() != null) && - (getBaseCalendar().isTimeIncluded(timeInMillis) == false)) { - return false; + (getBaseCalendar().isTimeIncluded(timeInMillis) == false)) { + return false; } return (!(cronExpression.isSatisfiedBy(new Date(timeInMillis)))); @@ -94,27 +137,28 @@ * @return the time in milliseconds representing the next time included * after the specified time. */ + @Override 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.getNextValidTimeAfter( - new Date(nextIncludedTime)).getTime(); - } else if ((getBaseCalendar() != null) && - (!getBaseCalendar().isTimeIncluded(nextIncludedTime))){ - nextIncludedTime = - getBaseCalendar().getNextIncludedTime(nextIncludedTime); - } else { - 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; @@ -126,14 +170,14 @@ * * @return the properteis of the CronCalendar in a String format */ + @Override public String toString() { StringBuffer buffer = new StringBuffer(); - buffer.append(getName()); - buffer.append(": base calendar: ["); + buffer.append("base calendar: ["); if (getBaseCalendar() != null) { - buffer.append(getBaseCalendar().toString()); + buffer.append(getBaseCalendar().toString()); } else { - buffer.append("null"); + buffer.append("null"); } buffer.append("], excluded cron expression: '"); buffer.append(cronExpression); @@ -149,7 +193,7 @@ * @see org.quartz.CronExpression */ public CronExpression getCronExpression() { - return cronExpression; + return cronExpression; } /** @@ -160,9 +204,9 @@ * if the string expression cannot be parsed */ public void setCronExpression(String expression) throws ParseException { - CronExpression newExp = new CronExpression(expression); - - this.cronExpression = newExp; + CronExpression newExp = new CronExpression(expression); + + this.cronExpression = newExp; } /** @@ -171,10 +215,10 @@ * @param expression the new cron expression */ public void setCronExpression(CronExpression expression) { - if (expression == null) { - throw new IllegalArgumentException("expression cannot be null"); - } - - this.cronExpression = expression; + if (expression == null) { + throw new IllegalArgumentException("expression cannot be null"); + } + + this.cronExpression = expression; } } \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/impl/calendar/DailyCalendar.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/calendar/DailyCalendar.java (.../DailyCalendar.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/calendar/DailyCalendar.java (.../DailyCalendar.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,7 +1,10 @@ package 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 @@ -22,9 +25,10 @@ * set of times that are excluded every day. * * @author Mike Funk, Aaron Craven - * @version $Revision$ $Date$ */ 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: "; @@ -34,7 +38,6 @@ private static final long oneMillis = 1; private static final String colon = ":"; - private String name; private int rangeStartingHourOfDay; private int rangeStartingMinute; private int rangeStartingSecond; @@ -65,18 +68,21 @@ * time. Note this means that a time range may not cross daily * boundaries (10PM - 2AM) * + * + *

+ * Note: This DailyCalendar will use the + * {@link TimeZone#getDefault()} time zone unless an explicit + * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)} + *

* - * @param name the name for the DailyCalendar * @param rangeStartingTime a String representing the starting time for the * time range * @param rangeEndingTime a String representing the ending time for the * the time range */ - public DailyCalendar(String name, - String rangeStartingTime, + public DailyCalendar(String rangeStartingTime, String rangeEndingTime) { super(); - this.name = name; setTimeRange(rangeStartingTime, rangeEndingTime); } @@ -100,7 +106,12 @@ * boundaries (10PM - 2AM) * * - * @param name the name for the DailyCalendar + *

+ * Note: This DailyCalendar will use the + * {@link TimeZone#getDefault()} time zone unless an explicit + * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)} + *

+ * * @param baseCalendar the base calendar for this calendar instance * – see {@link BaseCalendar} for more * information on base calendar functionality @@ -109,12 +120,10 @@ * @param rangeEndingTime a String representing the ending time for the * time range */ - public DailyCalendar(String name, - org.quartz.Calendar baseCalendar, + public DailyCalendar(org.quartz.Calendar baseCalendar, String rangeStartingTime, String rangeEndingTime) { super(baseCalendar); - this.name = name; setTimeRange(rangeStartingTime, rangeEndingTime); } @@ -132,7 +141,12 @@ * boundaries (10PM - 2AM) * * - * @param name the name for the DailyCalendar + *

+ * Note: This DailyCalendar will use the + * {@link TimeZone#getDefault()} time zone unless an explicit + * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)} + *

+ * * @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 @@ -144,8 +158,7 @@ * @param rangeEndingMillis the millisecond of the start of the time * range */ - public DailyCalendar(String name, - int rangeStartingHourOfDay, + public DailyCalendar(int rangeStartingHourOfDay, int rangeStartingMinute, int rangeStartingSecond, int rangeStartingMillis, @@ -154,7 +167,6 @@ int rangeEndingSecond, int rangeEndingMillis) { super(); - this.name = name; setTimeRange(rangeStartingHourOfDay, rangeStartingMinute, rangeStartingSecond, @@ -179,8 +191,12 @@ * boundaries (10PM - 2AM) * * - * @param name the name for the - * DailyCalendar + *

+ * Note: This DailyCalendar will use the + * {@link TimeZone#getDefault()} time zone unless an explicit + * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)} + *

+ * * @param baseCalendar the base calendar for this calendar * instance – see * {@link BaseCalendar} for more @@ -197,8 +213,7 @@ * @param rangeEndingMillis the millisecond of the start of the time * range */ - public DailyCalendar(String name, - org.quartz.Calendar baseCalendar, + public DailyCalendar(org.quartz.Calendar baseCalendar, int rangeStartingHourOfDay, int rangeStartingMinute, int rangeStartingSecond, @@ -208,7 +223,6 @@ int rangeEndingSecond, int rangeEndingMillis) { super(baseCalendar); - this.name = name; setTimeRange(rangeStartingHourOfDay, rangeStartingMinute, rangeStartingSecond, @@ -235,17 +249,21 @@ * true) * * - * @param name the name for the DailyCalendar + *

+ * Note: This DailyCalendar will use the + * {@link TimeZone#getDefault()} time zone unless an explicit + * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)} + *

+ * * @param rangeStartingCalendar a java.util.Calendar representing the * starting time for the time range * @param rangeEndingCalendar a java.util.Calendar representing the ending * time for the time range */ - public DailyCalendar(String name, + public DailyCalendar( Calendar rangeStartingCalendar, Calendar rangeEndingCalendar) { super(); - this.name = name; setTimeRange(rangeStartingCalendar, rangeEndingCalendar); } @@ -265,7 +283,12 @@ * true) * * - * @param name the name for the DailyCalendar + *

+ * Note: This DailyCalendar will use the + * {@link TimeZone#getDefault()} time zone unless an explicit + * time zone is set via {@link BaseCalendar#setTimeZone(TimeZone)} + *

+ * * @param baseCalendar the base calendar for this calendar instance * – see {@link BaseCalendar} for more * information on base calendar functionality @@ -274,12 +297,10 @@ * @param rangeEndingCalendar a java.util.Calendar representing the ending * time for the time range */ - public DailyCalendar(String name, - org.quartz.Calendar baseCalendar, + public DailyCalendar(org.quartz.Calendar baseCalendar, Calendar rangeStartingCalendar, Calendar rangeEndingCalendar) { super(baseCalendar); - this.name = name; setTimeRange(rangeStartingCalendar, rangeEndingCalendar); } @@ -297,20 +318,26 @@ * rangeEndingTime) * * - * @param name the name for the - * DailyCalendar + *

+ * 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(org.quartz.Calendar, java.util.TimeZone, long, long)} + * if you don't want the given rangeStartingTimeInMillis and + * rangeEndingTimeInMillis to be evaluated in the default + * time zone. + *

+ * * @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(String name, - long rangeStartingTimeInMillis, + public DailyCalendar(long rangeStartingTimeInMillis, long rangeEndingTimeInMillis) { super(); - this.name = name; setTimeRange(rangeStartingTimeInMillis, - rangeEndingTimeInMillis); + rangeEndingTimeInMillis); } /** @@ -327,8 +354,16 @@ * rangeEndingTime) * * - * @param name the name for the - * DailyCalendar + *

+ * 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(org.quartz.Calendar, java.util.TimeZone, long, long)} + * if you don't want the given rangeStartingTimeInMillis and + * rangeEndingTimeInMillis to be evaluated in the default + * time zone. + *

+ * * @param baseCalendar the base calendar for this calendar * instance – see {@link * BaseCalendar} for more information on @@ -338,25 +373,87 @@ * @param rangeEndingTimeInMillis a long representing the ending time for * the time range */ - public DailyCalendar(String name, - org.quartz.Calendar baseCalendar, + public DailyCalendar(org.quartz.Calendar baseCalendar, long rangeStartingTimeInMillis, long rangeEndingTimeInMillis) { super(baseCalendar); - this.name = name; setTimeRange(rangeStartingTimeInMillis, - rangeEndingTimeInMillis); + rangeEndingTimeInMillis); } + + /** + * Create a DailyCalendar with a time range defined by the + * specified values and no baseCalendar. The values are + * subject to the following considerations: + *
  • Only the time-of-day portion of the specified values will be + * used
  • + *
  • The starting time must be before the ending time of the defined + * time range. Note this means that a time range may not cross + * daily boundaries (10PM - 2AM). (because only time value are + * are used, it is possible for the two values to represent a valid + * time range and rangeStartingTime > + * rangeEndingTime)
  • + *
+ * + * @param timeZone the time zone for of the + * 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); + } /** - * Returns the name of the DailyCalendar + * Create a DailyCalendar with a time range defined by the + * specified values and the specified baseCalendar. The values + * are subject to the following considerations: + *
  • Only the time-of-day portion of the specified values will be + * used
  • + *
  • The starting time must be before the ending time of the defined + * time range. Note this means that a time range may not cross + * daily boundaries (10PM - 2AM). (because only time value are + * are used, it is possible for the two values to represent a valid + * time range and rangeStartingTime > + * rangeEndingTime)
  • + *
* - * @return the name of the DailyCalendar + * @param baseCalendar the base calendar for this calendar + * instance – see {@link + * BaseCalendar} for more information on + * base calendar functionality + * @param timeZone the time zone for of the + * 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 String getName() { - return name; + public DailyCalendar(org.quartz.Calendar baseCalendar, + TimeZone timeZone, + long rangeStartingTimeInMillis, + long rangeEndingTimeInMillis) { + super(baseCalendar, timeZone); + setTimeRange(rangeStartingTimeInMillis, + rangeEndingTimeInMillis); } + @Override + public Object clone() { + DailyCalendar clone = (DailyCalendar) super.clone(); + return clone; + } + /** * Determines whether the given time (in milliseconds) is 'included' by the * BaseCalendar @@ -365,35 +462,28 @@ * @return a boolean indicating whether the specified time is 'included' by * the BaseCalendar */ + @Override public boolean isTimeIncluded(long timeInMillis) { if ((getBaseCalendar() != null) && - (getBaseCalendar().isTimeIncluded(timeInMillis) == false)) { - return false; + (getBaseCalendar().isTimeIncluded(timeInMillis) == false)) { + return false; } - long startOfDayInMillis = getStartOfDayInMillis(timeInMillis); - long endOfDayInMillis = getEndOfDayInMillis(timeInMillis); + long startOfDayInMillis = getStartOfDayJavaCalendar(timeInMillis).getTime().getTime(); + long endOfDayInMillis = getEndOfDayJavaCalendar(timeInMillis).getTime().getTime(); long timeRangeStartingTimeInMillis = - getTimeRangeStartingTimeInMillis(timeInMillis); + getTimeRangeStartingTimeInMillis(timeInMillis); long timeRangeEndingTimeInMillis = - getTimeRangeEndingTimeInMillis(timeInMillis); + getTimeRangeEndingTimeInMillis(timeInMillis); if (!invertTimeRange) { - if ((timeInMillis > startOfDayInMillis && - timeInMillis < timeRangeStartingTimeInMillis) || - (timeInMillis > timeRangeEndingTimeInMillis && - timeInMillis < endOfDayInMillis)) { - - return true; - } else { - return false; - } + return + ((timeInMillis > startOfDayInMillis && + timeInMillis < timeRangeStartingTimeInMillis) || + (timeInMillis > timeRangeEndingTimeInMillis && + timeInMillis < endOfDayInMillis)); } else { - if ((timeInMillis >= timeRangeStartingTimeInMillis) && - (timeInMillis <= timeRangeEndingTimeInMillis)) { - return true; - } else { - return false; - } + return ((timeInMillis >= timeRangeStartingTimeInMillis) && + (timeInMillis <= timeRangeEndingTimeInMillis)); } } @@ -406,56 +496,57 @@ * @return the time in milliseconds representing the next time included * after the specified time. */ + @Override 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 = getEndOfDayInMillis(nextIncludedTime); - nextIncludedTime += 1l; - } else if ((getBaseCalendar() != null) && - (!getBaseCalendar().isTimeIncluded(nextIncludedTime))){ - nextIncludedTime = - getBaseCalendar().getNextIncludedTime(nextIncludedTime); - } else { - 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; @@ -471,13 +562,12 @@ * time range for the specified date. */ public long getTimeRangeStartingTimeInMillis(long timeInMillis) { - Calendar rangeStartingTime = Calendar.getInstance(); - rangeStartingTime.setTimeInMillis(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.getTimeInMillis(); + return rangeStartingTime.getTime().getTime(); } /** @@ -490,13 +580,12 @@ * time range for the specified date. */ public long getTimeRangeEndingTimeInMillis(long timeInMillis) { - Calendar rangeEndingTime = Calendar.getInstance(); - rangeEndingTime.setTimeInMillis(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.getTimeInMillis(); + return rangeEndingTime.getTime().getTime(); } /** @@ -506,7 +595,7 @@ * @return a boolean indicating whether the time range is inverted */ public boolean getInvertTimeRange() { - return invertTimeRange; + return invertTimeRange; } /** @@ -516,7 +605,7 @@ * @param flag the new value for the invertTimeRange flag. */ public void setInvertTimeRange(boolean flag) { - this.invertTimeRange = flag; + this.invertTimeRange = flag; } /** @@ -525,18 +614,17 @@ * * @return the properteis of the DailyCalendar in a String format */ + @Override public String toString() { - long todayInMillis = Calendar.getInstance().getTimeInMillis(); NumberFormat numberFormatter = NumberFormat.getNumberInstance(); numberFormatter.setMaximumFractionDigits(0); numberFormatter.setMinimumIntegerDigits(2); StringBuffer buffer = new StringBuffer(); - buffer.append(getName()); - buffer.append(": base calendar: ["); + buffer.append("base calendar: ["); if (getBaseCalendar() != null) { - buffer.append(getBaseCalendar().toString()); + buffer.append(getBaseCalendar().toString()); } else { - buffer.append("null"); + buffer.append("null"); } buffer.append("], time range: '"); buffer.append(numberFormatter.format(rangeStartingHourOfDay)); @@ -557,12 +645,25 @@ buffer.append(":"); numberFormatter.setMinimumIntegerDigits(3); buffer.append(numberFormatter.format(rangeEndingMillis)); - buffer.append("', inverted: " + - Boolean.toString(invertTimeRange) + "]"); + 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. * @@ -571,68 +672,68 @@ * @param rangeEndingTimeString a String representing the end time of the * excluded time range */ - private void setTimeRange(String rangeStartingTimeString, + 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 = rangeStartingTimeString.split(colon); + String[] rangeStartingTime; + int rStartingHourOfDay; + int rStartingMinute; + int rStartingSecond; + int rStartingMillis; + String[] rEndingTime; + int rEndingHourOfDay; + int rEndingMinute; + int rEndingSecond; + int rEndingMillis; + + rangeStartingTime = split(rangeStartingTimeString, colon); + if ((rangeStartingTime.length < 2) || (rangeStartingTime.length > 4)) { - throw new IllegalArgumentException("Invalid time string '" + - rangeStartingTimeString + "'"); + throw new IllegalArgumentException("Invalid time string '" + + rangeStartingTimeString + "'"); } - rangeStartingHourOfDay = Integer.parseInt(rangeStartingTime[0]); - rangeStartingMinute = Integer.parseInt(rangeStartingTime[1]); + rStartingHourOfDay = Integer.parseInt(rangeStartingTime[0]); + rStartingMinute = Integer.parseInt(rangeStartingTime[1]); if (rangeStartingTime.length > 2) { - rangeStartingSecond = Integer.parseInt(rangeStartingTime[2]); + rStartingSecond = Integer.parseInt(rangeStartingTime[2]); } else { - rangeStartingSecond = 0; + rStartingSecond = 0; } if (rangeStartingTime.length == 4) { - rangeStartingMillis = Integer.parseInt(rangeStartingTime[3]); + rStartingMillis = Integer.parseInt(rangeStartingTime[3]); } else { - rangeStartingMillis = 0; + rStartingMillis = 0; } - rangeEndingTime = rangeEndingTimeString.split(colon); + rEndingTime = split(rangeEndingTimeString, colon); - if ((rangeEndingTime.length < 2) || (rangeEndingTime.length > 4)) { - throw new IllegalArgumentException("Invalid time string '" + - rangeEndingTimeString + "'"); + if ((rEndingTime.length < 2) || (rEndingTime.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]); + rEndingHourOfDay = Integer.parseInt(rEndingTime[0]); + rEndingMinute = Integer.parseInt(rEndingTime[1]); + if (rEndingTime.length > 2) { + rEndingSecond = Integer.parseInt(rEndingTime[2]); } else { - rangeEndingSecond = 0; + rEndingSecond = 0; } - if (rangeEndingTime.length == 4) { - rangeEndingMillis = Integer.parseInt(rangeEndingTime[3]); + if (rEndingTime.length == 4) { + rEndingMillis = Integer.parseInt(rEndingTime[3]); } else { - rangeEndingMillis = 0; + rEndingMillis = 0; } - setTimeRange(rangeStartingHourOfDay, - rangeStartingMinute, - rangeStartingSecond, - rangeStartingMillis, - rangeEndingHourOfDay, - rangeEndingMinute, - rangeEndingSecond, - rangeEndingMillis); + setTimeRange(rStartingHourOfDay, + rStartingMinute, + rStartingSecond, + rStartingMillis, + rEndingHourOfDay, + rEndingMinute, + rEndingSecond, + rEndingMillis); } /** @@ -650,7 +751,7 @@ * @param rangeEndingMillis the millisecond of the start of the time * range */ - private void setTimeRange(int rangeStartingHourOfDay, + public void setTimeRange(int rangeStartingHourOfDay, int rangeStartingMinute, int rangeStartingSecond, int rangeStartingMillis, @@ -668,28 +769,28 @@ rangeEndingSecond, rangeEndingMillis); - Calendar startCal = Calendar.getInstance(); + 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 = Calendar.getInstance(); + 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); + rangeStartingHourOfDay + ":" + + rangeStartingMinute + ":" + + rangeStartingSecond + ":" + + rangeStartingMillis + separator + + rangeEndingHourOfDay + ":" + + rangeEndingMinute + ":" + + rangeEndingSecond + ":" + + rangeEndingMillis); } this.rangeStartingHourOfDay = rangeStartingHourOfDay; @@ -711,8 +812,8 @@ * @param rangeEndingCalendar a Calendar containing the end time for * the DailyCalendar */ - private void setTimeRange(Calendar rangeStartingCalendar, - Calendar rangeEndingCalendar) { + public void setTimeRange(Calendar rangeStartingCalendar, + Calendar rangeEndingCalendar) { setTimeRange( rangeStartingCalendar.get(Calendar.HOUR_OF_DAY), rangeStartingCalendar.get(Calendar.MINUTE), @@ -733,51 +834,14 @@ * @param rangeEndingTime the ending time (in milliseconds) for the time * range */ - private void setTimeRange(long rangeStartingTime, - long rangeEndingTime) { - Calendar startCal = Calendar.getInstance(); - Calendar endCal = Calendar.getInstance(); - startCal.setTimeInMillis(rangeStartingTime); - endCal.setTimeInMillis(rangeEndingTime); - - setTimeRange(startCal, endCal); + public void setTimeRange(long rangeStartingTime, + long rangeEndingTime) { + setTimeRange( + createJavaCalendar(rangeStartingTime), + createJavaCalendar(rangeEndingTime)); } /** - * Returns the start of the given day in milliseconds - * - * @param timeInMillis a time containing the desired date for the - * start-of-day time. - * @return the start of the given day in milliseconds - */ - private long getStartOfDayInMillis(long timeInMillis) { - Calendar startOfDay = Calendar.getInstance(); - startOfDay.setTimeInMillis(timeInMillis); - startOfDay.set(Calendar.HOUR_OF_DAY, 0); - startOfDay.set(Calendar.MINUTE, 0); - startOfDay.set(Calendar.SECOND, 0); - startOfDay.set(Calendar.MILLISECOND, 0); - return startOfDay.getTimeInMillis(); - } - - /** - * Returns the end of the given day in milliseconds - * - * @param timeInMillis a time containing the desired date for the - * end-of-day time. - * @return the end of the given day in milliseconds - */ - private long getEndOfDayInMillis(long timeInMillis) { - Calendar endOfDay = Calendar.getInstance(); - endOfDay.setTimeInMillis(timeInMillis); - endOfDay.set(Calendar.HOUR_OF_DAY, 23); - endOfDay.set(Calendar.MINUTE, 59); - endOfDay.set(Calendar.SECOND, 59); - endOfDay.set(Calendar.MILLISECOND, 999); - return endOfDay.getTimeInMillis(); - } - - /** * 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) Index: 3rdParty_sources/quartz/org/quartz/impl/calendar/HolidayCalendar.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/calendar/HolidayCalendar.java (.../HolidayCalendar.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/calendar/HolidayCalendar.java (.../HolidayCalendar.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,15 +15,13 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package 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 org.quartz.Calendar; @@ -45,23 +43,33 @@ */ 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(); + private TreeSet dates = new TreeSet(); - /** - * Constructor - */ public HolidayCalendar() { } - /** - * Constructor - */ public HolidayCalendar(Calendar baseCalendar) { - setBaseCalendar(baseCalendar); + super(baseCalendar); } + public HolidayCalendar(TimeZone timeZone) { + super(timeZone); + } + + public HolidayCalendar(Calendar baseCalendar, TimeZone timeZone) { + super(baseCalendar, timeZone); + } + + @Override + public Object clone() { + HolidayCalendar clone = (HolidayCalendar) super.clone(); + clone.dates = new TreeSet(dates); + return clone; + } + /** *

* Determine whether the given time (in milliseconds) is 'included' by the @@ -72,10 +80,13 @@ * Note that this Calendar is only has full-day precision. *

*/ + @Override public boolean isTimeIncluded(long timeStamp) { - if (super.isTimeIncluded(timeStamp) == false) return false; + if (super.isTimeIncluded(timeStamp) == false) { + return false; + } - Date lookFor = buildHoliday(new Date(timeStamp)); + Date lookFor = getStartOfDayJavaCalendar(timeStamp).getTime(); return !(dates.contains(lookFor)); } @@ -90,16 +101,17 @@ * Note that this Calendar is only has full-day precision. *

*/ + @Override public long getNextIncludedTime(long timeStamp) { // Call base calendar implementation first long baseTime = super.getNextIncludedTime(timeStamp); - if ((baseTime > 0) && (baseTime > timeStamp)) timeStamp = baseTime; + if ((baseTime > 0) && (baseTime > timeStamp)) { + timeStamp = baseTime; + } // Get timestamp for 00:00:00 - long newTimeStamp = buildHoliday(timeStamp); - - java.util.Calendar day = getJavaCalendar(newTimeStamp); + java.util.Calendar day = getStartOfDayJavaCalendar(timeStamp); while (isTimeIncluded(day.getTime().getTime()) == false) { day.add(java.util.Calendar.DATE, 1); } @@ -114,7 +126,7 @@ *

*/ public void addExcludedDate(Date excludedDate) { - Date date = buildHoliday(excludedDate); + Date date = getStartOfDayJavaCalendar(excludedDate.getTime()).getTime(); /* * System.err.println( "HolidayCalendar.add(): date=" + * excludedDate.toLocaleString()); @@ -123,7 +135,7 @@ } public void removeExcludedDate(Date dateToRemove) { - Date date = buildHoliday(dateToRemove); + Date date = getStartOfDayJavaCalendar(dateToRemove.getTime()).getTime(); dates.remove(date); } @@ -134,7 +146,7 @@ * significant. *

*/ - public SortedSet getExcludedDates() { + public SortedSet getExcludedDates() { return Collections.unmodifiableSortedSet(dates); } } Index: 3rdParty_sources/quartz/org/quartz/impl/calendar/MonthlyCalendar.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/calendar/MonthlyCalendar.java (.../MonthlyCalendar.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/calendar/MonthlyCalendar.java (.../MonthlyCalendar.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,87 +1,84 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.calendar; import java.io.Serializable; -import java.util.Date; +import java.util.TimeZone; import org.quartz.Calendar; /** *

* This implementation of the Calendar excludes a set of days of the month. You - * may use it to exclude every 1. of each month for example. But you may define + * may use it to exclude every first day of each month for example. But you may define * any day of a month. *

- * + * * @see org.quartz.Calendar * @see 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[31]; + private boolean[] excludeDays = new boolean[MAX_DAYS_IN_MONTH]; // Will be set to true, if all week days are excluded private boolean excludeAll = false; - /** - *

- * Constructor - *

- */ public MonthlyCalendar() { - super(); - init(); + this(null, null); } - /** - *

- * Constructor - *

- */ public MonthlyCalendar(Calendar baseCalendar) { - super(baseCalendar); - init(); + this(baseCalendar, null); } - /** - *

- * Initialize internal variables - *

- */ - private void init() { + public MonthlyCalendar(TimeZone timeZone) { + this(null, timeZone); + } + + public MonthlyCalendar(Calendar baseCalendar, TimeZone timeZone) { + super(baseCalendar, timeZone); + // all days are included by default excludeAll = areAllDaysExcluded(); } + @Override + public Object clone() { + MonthlyCalendar clone = (MonthlyCalendar) super.clone(); + clone.excludeDays = excludeDays.clone(); + return clone; + } + /** *

- * Get the array which defines the exclude-value of each day of month + * 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() { @@ -90,22 +87,37 @@ /** *

- * Return true, if mday is defined to be exluded. + * 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 of size greater or - * equal 31. + * 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) return; + 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(); } @@ -115,22 +127,29 @@ * 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) { - excludeDays[day] = 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. *

- * - * @return boolean */ public boolean areAllDaysExcluded() { - for (int i = 1; i <= 31; i++) { - if (isDayExcluded(i) == false) return false; + for (int i = 1; i <= MAX_DAYS_IN_MONTH; i++) { + if (isDayExcluded(i) == false) { + return false; + } } return true; @@ -141,20 +160,22 @@ * Determine whether the given time (in milliseconds) is 'included' by the * Calendar. *

- * + * *

* Note that this Calendar is only has full-day precision. *

*/ + @Override public boolean isTimeIncluded(long timeStamp) { - if (excludeAll == true) return false; + 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 = java.util.Calendar.getInstance(); - cl.setTime(new Date(timeStamp)); + java.util.Calendar cl = createJavaCalendar(timeStamp); int day = cl.get(java.util.Calendar.DAY_OF_MONTH); return !(isDayExcluded(day)); @@ -166,29 +187,34 @@ * 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. *

*/ + @Override public long getNextIncludedTime(long timeStamp) { - if (excludeAll == true) return 0; + if (excludeAll == true) { + return 0; + } // Call base calendar implementation first long baseTime = super.getNextIncludedTime(timeStamp); - if ((baseTime > 0) && (baseTime > timeStamp)) timeStamp = baseTime; + if ((baseTime > 0) && (baseTime > timeStamp)) { + timeStamp = baseTime; + } // Get timestamp for 00:00:00 - long newTimeStamp = buildHoliday(timeStamp); - - java.util.Calendar cl = getJavaCalendar(newTimeStamp); + 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 + 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_WEEK); + day = cl.get(java.util.Calendar.DAY_OF_MONTH); } return cl.getTime().getTime(); Index: 3rdParty_sources/quartz/org/quartz/impl/calendar/WeeklyCalendar.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/calendar/WeeklyCalendar.java (.../WeeklyCalendar.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/calendar/WeeklyCalendar.java (.../WeeklyCalendar.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,46 +1,42 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.calendar; import java.io.Serializable; -import java.util.Date; +import java.util.TimeZone; import 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. + * the week. By default it excludes SATURDAY and SUNDAY. *

- * + * * @see org.quartz.Calendar * @see 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. @@ -49,37 +45,33 @@ // Will be set to true, if all week days are excluded private boolean excludeAll = false; - /** - *

- * Constructor - *

- */ public WeeklyCalendar() { - super(); - init(); + this(null, null); } - /** - *

- * Constructor - *

- */ public WeeklyCalendar(Calendar baseCalendar) { - super(baseCalendar); - init(); + this(baseCalendar, null); } - /** - *

- * Initialize internal variables - *

- */ - private void init() { + 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(); } + @Override + public Object clone() { + WeeklyCalendar clone = (WeeklyCalendar) super.clone(); + clone.excludeDays = excludeDays.clone(); + return clone; + } + /** *

* Get the array with the week days @@ -107,7 +99,9 @@ *

*/ public void setDaysExcluded(boolean[] weekDays) { - if (weekDays == null) return; + if (weekDays == null) { + return; + } excludeDays = weekDays; excludeAll = areAllDaysExcluded(); @@ -129,46 +123,41 @@ *

* Check if all week days are excluded. That is no day is included. *

- * + * * @return boolean */ public boolean areAllDaysExcluded() { - if (isDayExcluded(java.util.Calendar.SUNDAY) == false) return false; - - if (isDayExcluded(java.util.Calendar.MONDAY) == false) return false; - - if (isDayExcluded(java.util.Calendar.TUESDAY) == false) return false; - - if (isDayExcluded(java.util.Calendar.WEDNESDAY) == false) return false; - - if (isDayExcluded(java.util.Calendar.THURSDAY) == false) return false; - - if (isDayExcluded(java.util.Calendar.FRIDAY) == false) return false; - - if (isDayExcluded(java.util.Calendar.SATURDAY) == false) return false; - - return true; + 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. *

*/ + @Override public boolean isTimeIncluded(long timeStamp) { - if (excludeAll == true) return false; + 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 = java.util.Calendar.getInstance(); - cl.setTime(new Date(timeStamp)); + java.util.Calendar cl = createJavaCalendar(timeStamp); int wday = cl.get(java.util.Calendar.DAY_OF_WEEK); return !(isDayExcluded(wday)); @@ -180,26 +169,30 @@ * 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. *

*/ + @Override public long getNextIncludedTime(long timeStamp) { - if (excludeAll == true) return 0; + if (excludeAll == true) { + return 0; + } // Call base calendar implementation first long baseTime = super.getNextIncludedTime(timeStamp); - if ((baseTime > 0) && (baseTime > timeStamp)) timeStamp = baseTime; + if ((baseTime > 0) && (baseTime > timeStamp)) { + timeStamp = baseTime; + } // Get timestamp for 00:00:00 - long newTimeStamp = buildHoliday(timeStamp); - - java.util.Calendar cl = getJavaCalendar(newTimeStamp); + 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 + if (!isDayExcluded(wday)) { + return timeStamp; // return the original value + } while (isDayExcluded(wday) == true) { cl.add(java.util.Calendar.DATE, 1); Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/AttributeRestoringConnectionInvocationHandler.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/AttributeRestoringConnectionInvocationHandler.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/AttributeRestoringConnectionInvocationHandler.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,166 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.jdbcjobstore; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

+ * 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). + *

+ * + * @see org.quartz.impl.jdbcjobstore.JobStoreSupport#getConnection() + * @see org.quartz.impl.jdbcjobstore.JobStoreCMT#getNonManagedTXConnection() + */ +public class AttributeRestoringConnectionInvocationHandler implements InvocationHandler { + private Connection conn; + + private boolean overwroteOriginalAutoCommitValue; + private boolean overwroteOriginalTxIsolationValue; + + // Set if overwroteOriginalAutoCommitValue is true + private boolean originalAutoCommitValue; + + // Set if overwroteOriginalTxIsolationValue is true + private int originalTxIsolationValue; + + public AttributeRestoringConnectionInvocationHandler( + Connection conn) { + this.conn = conn; + } + + protected Logger getLog() { + return LoggerFactory.getLogger(getClass()); + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + if (method.getName().equals("setAutoCommit")) { + setAutoCommit(((Boolean)args[0]).booleanValue()); + } else if (method.getName().equals("setTransactionIsolation")) { + setTransactionIsolation(((Integer)args[0]).intValue()); + } else if (method.getName().equals("close")) { + close(); + } else { + try { + return method.invoke(conn, args); + } + catch(InvocationTargetException ite) { + throw (ite.getCause() != null ? ite.getCause() : ite); + } + + } + + return null; + } + + /** + * Sets this connection's auto-commit mode to the given state, saving + * the original mode. The connection's original auto commit mode is restored + * when the connection is closed. + */ + public void setAutoCommit(boolean autoCommit) throws SQLException { + boolean currentAutoCommitValue = conn.getAutoCommit(); + + if (autoCommit != currentAutoCommitValue) { + if (overwroteOriginalAutoCommitValue == false) { + overwroteOriginalAutoCommitValue = true; + originalAutoCommitValue = currentAutoCommitValue; + } + + conn.setAutoCommit(autoCommit); + } + } + + /** + * Attempts to change the transaction isolation level to the given level, saving + * the original level. The connection's original transaction isolation level is + * restored when the connection is closed. + */ + public void setTransactionIsolation(int level) throws SQLException { + int currentLevel = conn.getTransactionIsolation(); + + if (level != currentLevel) { + if (overwroteOriginalTxIsolationValue == false) { + overwroteOriginalTxIsolationValue = true; + originalTxIsolationValue = currentLevel; + } + + conn.setTransactionIsolation(level); + } + } + + /** + * Gets the underlying connection to which all operations ultimately + * defer. This is provided in case a user ever needs to punch through + * the wrapper to access vendor specific methods outside of the + * standard 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(); + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CUBRIDDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CUBRIDDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CUBRIDDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,134 @@ +/* + * Copyright Terracotta, Inc. + * + * 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 org.quartz.impl.jdbcjobstore; + +import com.mchange.v2.c3p0.C3P0ProxyConnection; +import java.io.*; +import java.lang.reflect.Method; +import java.sql.*; +import org.quartz.spi.ClassLoadHelper; +import org.slf4j.Logger; + +/** + *

This is a driver delegate for the CUBRID JDBC driver. For Quartz 2.x

+ * Blob handling instructions at + * http://www.cubrid.org/manual/831/en/Using%20BLOB|CLOB Also at + * http://www.cubrid.org/wiki_tutorials/entry/working-with-cubrid-blob-clob-data-types + * + * @author Timothy Anyona + */ +public class CUBRIDDelegate extends StdJDBCDelegate { + + /** + *

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.

+ * + * @param rs the result set, already queued to the correct row + * @param colName the column name for the BLOB + * @return the deserialized Object from the ResultSet BLOB + * @throws ClassNotFoundException if a class found during deserialization + * cannot be found + * @throws IOException if deserialization causes an error + */ + @Override + protected Object getObjectFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { + + Object obj = null; + InputStream binaryInput; + + Blob blob = rs.getBlob(colName); + byte[] bytes = blob.getBytes(1, (int) blob.length()); + + if (bytes != null && bytes.length != 0) { + binaryInput = new ByteArrayInputStream(bytes); + + ObjectInputStream in = new ObjectInputStream(binaryInput); + try { + obj = in.readObject(); + } finally { + in.close(); + } + } + + return obj; + } + + @Override + protected Object getJobDataFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { + + if (canUseProperties()) { + InputStream binaryInput; + + Blob blob = rs.getBlob(colName); + byte[] bytes = blob.getBytes(1, (int) blob.length()); + + if (bytes == null || bytes.length == 0) { + return null; + } + binaryInput = new ByteArrayInputStream(bytes); + return binaryInput; + } + + return getObjectFromBlob(rs, colName); + } + + /** + * 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. + */ + @Override + protected void setBytes(PreparedStatement ps, int index, ByteArrayOutputStream baos) + throws SQLException { + + byte[] byteArray; + if (baos == null) { + //saving 0 byte blob may cause error? like http://dev.naver.com/projects/cubrid/issue/13710 - (0 byte bit) + //alternativly store null since blob not null columns are not allowed (cubrid 8.4.1). may be allowed in future versions? + byteArray = new byte[0]; + } else { + byteArray = baos.toByteArray(); + } + + //quartz 2.x uses c3p0, c3p0 doesn't support createBlob method as of 0.9.2 + Connection conn = ps.getConnection(); + if (conn instanceof C3P0ProxyConnection) { + try { + C3P0ProxyConnection c3p0Conn = (C3P0ProxyConnection) conn; + Method m = Connection.class.getMethod("createBlob", new Class[]{}); //will call createBlob method on the underlying connection + Object[] args = new Object[]{}; //arguments to be passed to the method. none in this case + Blob blob = (Blob) c3p0Conn.rawConnectionOperation(m, C3P0ProxyConnection.RAW_CONNECTION, args); + blob.setBytes(1, byteArray); + ps.setBlob(index, blob); + } catch (Exception ex) { + ex.printStackTrace(); + } + } else { + Blob blob = ps.getConnection().createBlob(); + blob.setBytes(1, byteArray); + ps.setBlob(index, blob); + } + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CacheDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CacheDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CacheDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,179 @@ +/* + * Copyright 2001-2012 Terracotta, Inc. + * + * 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 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.sql.Blob; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + *

+ * This is a driver delegate for Intersystems Caché database. + *

+ * + *

+ * Works with the Oracle table creation scripts / schema. + *

+ * + * @author Franck Routier + * @author Franck Routier + */ +public class CacheDelegate extends StdJDBCDelegate { + + //--------------------------------------------------------------------------- + // protected methods that can be overridden by subclasses + //--------------------------------------------------------------------------- + + /** + * 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. + */ + @Override + protected void setBytes(PreparedStatement ps, int index, ByteArrayOutputStream baos) throws SQLException { + ps.setObject(index, ((baos == null) ? null : baos.toByteArray()), java.sql.Types.BLOB); + } + + /** + * {@inheritDoc} + *

+ * Caché requires {@code java.sql.Blob} instances to be explicitly freed. + */ + @Override + protected Object getObjectFromBlob(ResultSet rs, String colName) throws ClassNotFoundException, IOException, SQLException { + Blob blob = rs.getBlob(colName); + if (blob == null) { + return null; + } else { + try { + if (blob.length() == 0) { + return null; + } else { + InputStream binaryInput = blob.getBinaryStream(); + if (binaryInput == null) { + return null; + } else if (binaryInput instanceof ByteArrayInputStream && ((ByteArrayInputStream) binaryInput).available() == 0 ) { + return null; + } else { + ObjectInputStream in = new ObjectInputStream(binaryInput); + try { + return in.readObject(); + } finally { + in.close(); + } + } + } + } finally { + blob.free(); + } + } + } + + /** + * {@inheritDoc} + *

+ * Caché requires {@code java.sql.Blob} instances to be explicitly freed. + */ + @Override + protected Object getJobDataFromBlob(ResultSet rs, String colName) throws ClassNotFoundException, IOException, SQLException { + if (canUseProperties()) { + Blob blob = rs.getBlob(colName); + if (blob == null) { + return null; + } else { + return new BlobFreeingStream(blob, blob.getBinaryStream()); + } + } else { + return getObjectFromBlob(rs, colName); + } + } + + private static class BlobFreeingStream extends InputStream { + + private final Blob source; + private final InputStream delegate; + + private BlobFreeingStream(Blob blob, InputStream stream) { + this.source = blob; + this.delegate = stream; + } + + @Override + public int read() throws IOException { + return delegate.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return delegate.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return delegate.read(b, off, len); + } + + @Override + public long skip(long n) throws IOException { + return delegate.skip(n); + } + + @Override + public int available() throws IOException { + return delegate.available(); + } + + @Override + public void close() throws IOException { + try { + delegate.close(); + } finally { + try { + source.free(); + } catch (SQLException ex) { + throw new IOException(ex); + } + } + } + + @Override + public synchronized void mark(int readlimit) { + delegate.mark(readlimit); + } + + @Override + public synchronized void reset() throws IOException { + delegate.reset(); + } + + @Override + public boolean markSupported() { + return delegate.markSupported(); + } + } +} + Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CalendarIntervalTriggerPersistenceDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CalendarIntervalTriggerPersistenceDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CalendarIntervalTriggerPersistenceDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,60 @@ +package org.quartz.impl.jdbcjobstore; + +import java.util.TimeZone; + +import org.quartz.CalendarIntervalScheduleBuilder; +import org.quartz.ScheduleBuilder; +import org.quartz.DateBuilder.IntervalUnit; +import org.quartz.impl.triggers.CalendarIntervalTriggerImpl; +import org.quartz.spi.OperableTrigger; + +public class CalendarIntervalTriggerPersistenceDelegate extends SimplePropertiesTriggerPersistenceDelegateSupport { + + public boolean canHandleTriggerType(OperableTrigger trigger) { + return ((trigger instanceof CalendarIntervalTriggerImpl) && !((CalendarIntervalTriggerImpl)trigger).hasAdditionalProperties()); + } + + public String getHandledTriggerTypeDiscriminator() { + return TTYPE_CAL_INT; + } + + @Override + protected SimplePropertiesTriggerProperties getTriggerProperties(OperableTrigger trigger) { + + CalendarIntervalTriggerImpl calTrig = (CalendarIntervalTriggerImpl)trigger; + + SimplePropertiesTriggerProperties props = new SimplePropertiesTriggerProperties(); + + props.setInt1(calTrig.getRepeatInterval()); + props.setString1(calTrig.getRepeatIntervalUnit().name()); + props.setInt2(calTrig.getTimesTriggered()); + props.setString2(calTrig.getTimeZone().getID()); + props.setBoolean1(calTrig.isPreserveHourOfDayAcrossDaylightSavings()); + props.setBoolean2(calTrig.isSkipDayIfHourDoesNotExist()); + + return props; + } + + @Override + protected TriggerPropertyBundle getTriggerPropertyBundle(SimplePropertiesTriggerProperties props) { + + TimeZone tz = null; // if we use null, that's ok as system default tz will be used + String tzId = props.getString2(); + if(tzId != null && tzId.trim().length() != 0) // there could be null entries from previously released versions + tz = TimeZone.getTimeZone(tzId); + + ScheduleBuilder sb = CalendarIntervalScheduleBuilder.calendarIntervalSchedule() + .withInterval(props.getInt1(), IntervalUnit.valueOf(props.getString1())) + .inTimeZone(tz) + .preserveHourOfDayAcrossDaylightSavings(props.isBoolean1()) + .skipDayIfHourDoesNotExist(props.isBoolean2()); + + int timesTriggered = props.getInt2(); + + String[] statePropertyNames = { "timesTriggered" }; + Object[] statePropertyValues = { timesTriggered }; + + return new TriggerPropertyBundle(sb, statePropertyNames, statePropertyValues); + } + +} Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CloudscapeDelegate.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/Constants.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/Constants.java (.../Constants.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/Constants.java (.../Constants.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,10 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ - package org.quartz.impl.jdbcjobstore; /** @@ -42,147 +38,158 @@ */ // Table names - public static final String TABLE_JOB_DETAILS = "JOB_DETAILS"; + String TABLE_JOB_DETAILS = "JOB_DETAILS"; - public static final String TABLE_TRIGGERS = "TRIGGERS"; + String TABLE_TRIGGERS = "TRIGGERS"; - public static final String TABLE_SIMPLE_TRIGGERS = "SIMPLE_TRIGGERS"; + String TABLE_SIMPLE_TRIGGERS = "SIMPLE_TRIGGERS"; - public static final String TABLE_CRON_TRIGGERS = "CRON_TRIGGERS"; + String TABLE_CRON_TRIGGERS = "CRON_TRIGGERS"; - public static final String TABLE_BLOB_TRIGGERS = "BLOB_TRIGGERS"; + String TABLE_BLOB_TRIGGERS = "BLOB_TRIGGERS"; - public static final String TABLE_FIRED_TRIGGERS = "FIRED_TRIGGERS"; + String TABLE_FIRED_TRIGGERS = "FIRED_TRIGGERS"; - public static final String TABLE_JOB_LISTENERS = "JOB_LISTENERS"; + String TABLE_CALENDARS = "CALENDARS"; - public static final String TABLE_TRIGGER_LISTENERS = "TRIGGER_LISTENERS"; + String TABLE_PAUSED_TRIGGERS = "PAUSED_TRIGGER_GRPS"; - public static final String TABLE_CALENDARS = "CALENDARS"; + String TABLE_LOCKS = "LOCKS"; - public static final String TABLE_PAUSED_TRIGGERS = "PAUSED_TRIGGER_GRPS"; + String TABLE_SCHEDULER_STATE = "SCHEDULER_STATE"; - public static final String TABLE_LOCKS = "LOCKS"; - - public static final String TABLE_SCHEDULER_STATE = "SCHEDULER_STATE"; - // TABLE_JOB_DETAILS columns names - public static final String COL_JOB_NAME = "JOB_NAME"; + + String COL_SCHEDULER_NAME = "SCHED_NAME"; + + String COL_JOB_NAME = "JOB_NAME"; - public static final String COL_JOB_GROUP = "JOB_GROUP"; + String COL_JOB_GROUP = "JOB_GROUP"; - public static final String COL_IS_DURABLE = "IS_DURABLE"; + String COL_IS_DURABLE = "IS_DURABLE"; - public static final String COL_IS_VOLATILE = "IS_VOLATILE"; + String COL_IS_VOLATILE = "IS_VOLATILE"; - public static final String COL_IS_STATEFUL = "IS_STATEFUL"; + String COL_IS_NONCONCURRENT = "IS_NONCONCURRENT"; - public static final String COL_REQUESTS_RECOVERY = "REQUESTS_RECOVERY"; + String COL_IS_UPDATE_DATA = "IS_UPDATE_DATA"; - public static final String COL_JOB_DATAMAP = "JOB_DATA"; + String COL_REQUESTS_RECOVERY = "REQUESTS_RECOVERY"; - public static final String COL_JOB_CLASS = "JOB_CLASS_NAME"; + String COL_JOB_DATAMAP = "JOB_DATA"; - public static final String COL_DESCRIPTION = "DESCRIPTION"; + String COL_JOB_CLASS = "JOB_CLASS_NAME"; - // TABLE_JOB_LISTENERS columns names - public static final String COL_JOB_LISTENER = "JOB_LISTENER"; + String COL_DESCRIPTION = "DESCRIPTION"; // TABLE_TRIGGERS columns names - public static final String COL_TRIGGER_NAME = "TRIGGER_NAME"; + String COL_TRIGGER_NAME = "TRIGGER_NAME"; - public static final String COL_TRIGGER_GROUP = "TRIGGER_GROUP"; + String COL_TRIGGER_GROUP = "TRIGGER_GROUP"; - public static final String COL_NEXT_FIRE_TIME = "NEXT_FIRE_TIME"; + String COL_NEXT_FIRE_TIME = "NEXT_FIRE_TIME"; - public static final String COL_PREV_FIRE_TIME = "PREV_FIRE_TIME"; + String COL_PREV_FIRE_TIME = "PREV_FIRE_TIME"; - public static final String COL_TRIGGER_STATE = "TRIGGER_STATE"; + String COL_TRIGGER_STATE = "TRIGGER_STATE"; - public static final String COL_TRIGGER_TYPE = "TRIGGER_TYPE"; + String COL_TRIGGER_TYPE = "TRIGGER_TYPE"; - public static final String COL_START_TIME = "START_TIME"; + String COL_START_TIME = "START_TIME"; - public static final String COL_END_TIME = "END_TIME"; + String COL_END_TIME = "END_TIME"; - public static final String COL_MISFIRE_INSTRUCTION = "MISFIRE_INSTR"; + String COL_PRIORITY = "PRIORITY"; - public static final String ALIAS_COL_NEXT_FIRE_TIME = "ALIAS_NXT_FR_TM"; + String COL_MISFIRE_INSTRUCTION = "MISFIRE_INSTR"; + String ALIAS_COL_NEXT_FIRE_TIME = "ALIAS_NXT_FR_TM"; + // TABLE_SIMPLE_TRIGGERS columns names - public static final String COL_REPEAT_COUNT = "REPEAT_COUNT"; + String COL_REPEAT_COUNT = "REPEAT_COUNT"; - public static final String COL_REPEAT_INTERVAL = "REPEAT_INTERVAL"; + String COL_REPEAT_INTERVAL = "REPEAT_INTERVAL"; - public static final String COL_TIMES_TRIGGERED = "TIMES_TRIGGERED"; + String COL_TIMES_TRIGGERED = "TIMES_TRIGGERED"; // TABLE_CRON_TRIGGERS columns names - public static final String COL_CRON_EXPRESSION = "CRON_EXPRESSION"; + String COL_CRON_EXPRESSION = "CRON_EXPRESSION"; // TABLE_BLOB_TRIGGERS columns names - public static final String COL_BLOB = "BLOB_DATA"; + String COL_BLOB = "BLOB_DATA"; - public static final String COL_TIME_ZONE_ID = "TIME_ZONE_ID"; + String COL_TIME_ZONE_ID = "TIME_ZONE_ID"; - // TABLE_TRIGGER_LISTENERS - public static final String COL_TRIGGER_LISTENER = "TRIGGER_LISTENER"; - // TABLE_FIRED_TRIGGERS columns names - public static final String COL_INSTANCE_NAME = "INSTANCE_NAME"; + String COL_INSTANCE_NAME = "INSTANCE_NAME"; - public static final String COL_FIRED_TIME = "FIRED_TIME"; + String COL_FIRED_TIME = "FIRED_TIME"; - public static final String COL_ENTRY_ID = "ENTRY_ID"; + String COL_SCHED_TIME = "SCHED_TIME"; + + String COL_ENTRY_ID = "ENTRY_ID"; - public static final String COL_ENTRY_STATE = "STATE"; + String COL_ENTRY_STATE = "STATE"; // TABLE_CALENDARS columns names - public static final String COL_CALENDAR_NAME = "CALENDAR_NAME"; + String COL_CALENDAR_NAME = "CALENDAR_NAME"; - public static final String COL_CALENDAR = "CALENDAR"; + String COL_CALENDAR = "CALENDAR"; // TABLE_LOCKS columns names - public static final String COL_LOCK_NAME = "LOCK_NAME"; + String COL_LOCK_NAME = "LOCK_NAME"; // TABLE_LOCKS columns names - public static final String COL_LAST_CHECKIN_TIME = "LAST_CHECKIN_TIME"; + String COL_LAST_CHECKIN_TIME = "LAST_CHECKIN_TIME"; - public static final String COL_CHECKIN_INTERVAL = "CHECKIN_INTERVAL"; + String COL_CHECKIN_INTERVAL = "CHECKIN_INTERVAL"; - public static final String COL_RECOVERER = "RECOVERER"; - // MISC CONSTANTS - public static final String DEFAULT_TABLE_PREFIX = "QRTZ_"; + String DEFAULT_TABLE_PREFIX = "QRTZ_"; // STATES - public final static String STATE_WAITING = "WAITING"; + String STATE_WAITING = "WAITING"; - public final static String STATE_ACQUIRED = "ACQUIRED"; + String STATE_ACQUIRED = "ACQUIRED"; - public final static String STATE_EXECUTING = "EXECUTING"; + String STATE_EXECUTING = "EXECUTING"; - public final static String STATE_COMPLETE = "COMPLETE"; + String STATE_COMPLETE = "COMPLETE"; - public final static String STATE_BLOCKED = "BLOCKED"; + String STATE_BLOCKED = "BLOCKED"; - public final static String STATE_ERROR = "ERROR"; + String STATE_ERROR = "ERROR"; - public final static String STATE_PAUSED = "PAUSED"; + String STATE_PAUSED = "PAUSED"; - public final static String STATE_PAUSED_BLOCKED = "PAUSED_BLOCKED"; + String STATE_PAUSED_BLOCKED = "PAUSED_BLOCKED"; - public final static String STATE_DELETED = "DELETED"; + String STATE_DELETED = "DELETED"; - public final static String STATE_MISFIRED = "MISFIRED"; + /** + * @deprecated Whether a trigger has misfired is no longer a state, but + * rather now identified dynamically by whether the trigger's next fire + * time is more than the misfire threshold time in the past. + */ + String STATE_MISFIRED = "MISFIRED"; - public final static String ALL_GROUPS_PAUSED = "_$_ALL_GROUPS_PAUSED_$_"; + String ALL_GROUPS_PAUSED = "_$_ALL_GROUPS_PAUSED_$_"; // TRIGGER TYPES - public final static String TTYPE_SIMPLE = "SIMPLE"; + /** Simple Trigger type. */ + String TTYPE_SIMPLE = "SIMPLE"; - public final static String TTYPE_CRON = "CRON"; + /** Cron Trigger type. */ + String TTYPE_CRON = "CRON"; - public final static String TTYPE_BLOB = "BLOB"; + /** Calendar Interval Trigger type. */ + String TTYPE_CAL_INT = "CAL_INT"; + + /** Daily Time Interval Trigger type. */ + String TTYPE_DAILY_TIME_INT = "DAILY_I"; + + /** A general blob Trigger type. */ + String TTYPE_BLOB = "BLOB"; } // EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CronTriggerPersistenceDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CronTriggerPersistenceDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/CronTriggerPersistenceDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,118 @@ +package org.quartz.impl.jdbcjobstore; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.TimeZone; + +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.JobDetail; +import org.quartz.TriggerKey; +import org.quartz.impl.triggers.CronTriggerImpl; +import org.quartz.spi.OperableTrigger; + +public class CronTriggerPersistenceDelegate implements TriggerPersistenceDelegate, StdJDBCConstants { + + protected String tablePrefix; + protected String schedNameLiteral; + + public void initialize(String theTablePrefix, String schedName) { + this.tablePrefix = theTablePrefix; + this.schedNameLiteral = "'" + schedName + "'"; + } + + public String getHandledTriggerTypeDiscriminator() { + return TTYPE_CRON; + } + + public boolean canHandleTriggerType(OperableTrigger trigger) { + return ((trigger instanceof CronTriggerImpl) && !((CronTriggerImpl)trigger).hasAdditionalProperties()); + } + + public int deleteExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException { + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(Util.rtp(DELETE_CRON_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); + + return ps.executeUpdate(); + } finally { + Util.closeStatement(ps); + } + } + + public int insertExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { + + CronTrigger cronTrigger = (CronTrigger)trigger; + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(Util.rtp(INSERT_CRON_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + ps.setString(3, cronTrigger.getCronExpression()); + ps.setString(4, cronTrigger.getTimeZone().getID()); + + return ps.executeUpdate(); + } finally { + Util.closeStatement(ps); + } + } + + public TriggerPropertyBundle loadExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException { + + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(Util.rtp(SELECT_CRON_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); + rs = ps.executeQuery(); + + if (rs.next()) { + String cronExpr = rs.getString(COL_CRON_EXPRESSION); + String timeZoneId = rs.getString(COL_TIME_ZONE_ID); + + CronScheduleBuilder cb = CronScheduleBuilder.cronSchedule(cronExpr); + + if (timeZoneId != null) + cb.inTimeZone(TimeZone.getTimeZone(timeZoneId)); + + return new TriggerPropertyBundle(cb, null, null); + } + + throw new IllegalStateException("No record found for selection of Trigger with key: '" + triggerKey + "' and statement: " + Util.rtp(SELECT_CRON_TRIGGER, tablePrefix, schedNameLiteral)); + } finally { + Util.closeResultSet(rs); + Util.closeStatement(ps); + } + } + + public int updateExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { + + CronTrigger cronTrigger = (CronTrigger)trigger; + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(Util.rtp(UPDATE_CRON_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, cronTrigger.getCronExpression()); + ps.setString(2, cronTrigger.getTimeZone().getID()); + ps.setString(3, trigger.getKey().getName()); + ps.setString(4, trigger.getKey().getGroup()); + + return ps.executeUpdate(); + } finally { + Util.closeStatement(ps); + } + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DB2v6Delegate.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DB2v6Delegate.java (.../DB2v6Delegate.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DB2v6Delegate.java (.../DB2v6Delegate.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,18 +15,16 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; -import org.apache.commons.logging.Log; +import org.quartz.JobKey; +import org.quartz.spi.ClassLoadHelper; +import org.slf4j.Logger; /** * Quartz JDBC delegate for DB2 v6 databases. select count(name) @@ -36,31 +34,32 @@ * @author James House */ public class DB2v6Delegate extends StdJDBCDelegate { + @SuppressWarnings("hiding") public static final String SELECT_NUM_JOBS = "SELECT COUNT(*) FROM " - + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS; + + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + @SuppressWarnings("hiding") public static final String SELECT_NUM_TRIGGERS_FOR_JOB = "SELECT COUNT(*) FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; + @SuppressWarnings("hiding") public static final String SELECT_NUM_TRIGGERS = "SELECT COUNT(*) FROM " - + TABLE_PREFIX_SUBST + TABLE_TRIGGERS; + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + @SuppressWarnings("hiding") public static final String SELECT_NUM_CALENDARS = "SELECT COUNT(*) FROM " - + TABLE_PREFIX_SUBST + TABLE_CALENDARS; + + TABLE_PREFIX_SUBST + TABLE_CALENDARS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - 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); - } - + @Override public int selectNumJobs(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -76,19 +75,20 @@ return count; } finally { - close(ps); + closeResultSet(rs); + closeStatement(ps); } } - public int selectNumTriggersForJob(Connection conn, String jobName, - String groupName) throws SQLException { + @Override + public int selectNumTriggersForJob(Connection conn, JobKey jobKey) 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); + ps.setString(1, jobKey.getName()); + ps.setString(2, jobKey.getGroup()); rs = ps.executeQuery(); if (rs.next()) { @@ -97,10 +97,12 @@ return 0; } } finally { - close(ps); + closeResultSet(rs); + closeStatement(ps); } } + @Override public int selectNumTriggers(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -116,10 +118,12 @@ return count; } finally { - close(ps); + closeResultSet(rs); + closeStatement(ps); } } + @Override public int selectNumCalendars(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -135,18 +139,10 @@ return count; } finally { - close(ps); + closeResultSet(rs); + closeStatement(ps); } } - - private void close(Statement stmt) { - if (stmt != null) { - try { - stmt.close(); - } catch (SQLException ignore) { - } - } - } } // EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DB2v7Delegate.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DB2v7Delegate.java (.../DB2v7Delegate.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DB2v7Delegate.java (.../DB2v7Delegate.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,536 +15,49 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.sql.Connection; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Date; -import org.quartz.impl.jdbcjobstore.StdJDBCDelegate; -import org.apache.commons.logging.Log; -import org.quartz.Calendar; -import org.quartz.CronTrigger; -import org.quartz.JobDataMap; -import org.quartz.JobDetail; -import org.quartz.Scheduler; -import org.quartz.SimpleTrigger; -import org.quartz.Trigger; -import org.quartz.utils.Key; +import org.quartz.spi.ClassLoadHelper; +import org.slf4j.Logger; /** * 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[])}. + *

* * @author Blair Jensen */ public class DB2v7Delegate extends StdJDBCDelegate { - public DB2v7Delegate(Log logger, String tablePrefix, String instanceId) { - super(logger, tablePrefix, instanceId); + /** + * Sets the designated parameter to the byte array of the given + * 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. + */ + @Override + protected void setBytes(PreparedStatement ps, int index, ByteArrayOutputStream baos) throws SQLException { + ps.setObject(index, ((baos == null) ? null : baos.toByteArray()), java.sql.Types.BLOB); } - public DB2v7Delegate(Log log, String tablePrefix, String instanceId, - Boolean useProperties) { - super(log, tablePrefix, instanceId, useProperties); + /** + * Sets the designated parameter to the given Java boolean value. + * This translates the boolean to 1/0 for true/false. + */ + @Override + protected void setBoolean(PreparedStatement ps, int index, boolean val) throws SQLException { + ps.setString(index, ((val) ? "1" : "0")); } - 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); - ps.setString(2, "1"); - //ps.setBoolean(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); - SimpleTrigger rcvryTrig = new SimpleTrigger("recover_" - + instanceId + "_" + String.valueOf(dumId++), - Scheduler.DEFAULT_RECOVERY_GROUP, new Date(firedTime)); - rcvryTrig.setJobName(jobName); - rcvryTrig.setJobGroup(jobGroup); - rcvryTrig - .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW); - - JobDataMap jd = selectTriggerJobDataMap(conn, trigName, trigGroup); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_NAME", trigName); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_GROUP", trigGroup); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_FIRETIME_IN_MILLISECONDS_AS_STRING", 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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - 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()); - ps.setString(5, toBooleanIntString(job.isDurable())); - ps.setString(6, toBooleanIntString(job.isVolatile())); - ps.setString(7, toBooleanIntString(job.isStateful())); - ps.setString(8, toBooleanIntString(job.requestsRecovery())); - //ps.setBoolean (5, job.isDurable()); - //ps.setBoolean (6, job.isVolatile()); - //ps.setBoolean (7, job.isStateful()); - //ps.setBoolean (8, job.requestsRecovery()); - ps.setObject(9, baos.toByteArray(), java.sql.Types.BLOB); - //ps.setBytes (9, baos.toByteArray()); - - insertResult = ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - - if (insertResult > 0) { - String[] jobListeners = job.getJobListenerNames(); - for (int i = 0; jobListeners != null && i < jobListeners.length; i++) - insertJobListener(conn, job, jobListeners[i]); - } - - return insertResult; - } - - 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()); - ps.setString(3, toBooleanIntString(job.isDurable())); - ps.setString(4, toBooleanIntString(job.isVolatile())); - ps.setString(5, toBooleanIntString(job.isStateful())); - ps.setString(6, toBooleanIntString(job.requestsRecovery())); - //ps.setBoolean (3, job.isDurable()); - //ps.setBoolean (4, job.isVolatile()); - //ps.setBoolean (5, job.isStateful()); - //ps.setBoolean (6, job.requestsRecovery()); - ps.setObject(7, baos.toByteArray(), java.sql.Types.BLOB); - //ps.setBytes (7, baos.toByteArray()); - ps.setString(8, job.getName()); - ps.setString(9, job.getGroup()); - - insertResult = ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - - 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()); - - 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()); - ps.setString(5, toBooleanIntString(trigger.isVolatile())); - //ps.setBoolean(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) { - ps.setString(10, TTYPE_SIMPLE); - } else if (trigger instanceof CronTrigger) { - ps.setString(10, TTYPE_CRON); - } else { // (trigger instanceof BlobTrigger) - 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.setObject(15, baos.toByteArray(), java.sql.Types.BLOB); - - insertResult = ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - - 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()); - - PreparedStatement ps = null; - - int insertResult = 0; - - try { - ps = conn.prepareStatement(rtp(UPDATE_TRIGGER)); - ps.setString(1, trigger.getJobName()); - ps.setString(2, trigger.getJobGroup()); - ps.setString(3, toBooleanIntString(trigger.isVolatile())); - //ps.setBoolean(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) { - // updateSimpleTrigger(conn, (SimpleTrigger)trigger); - ps.setString(8, TTYPE_SIMPLE); - } else if (trigger instanceof CronTrigger) { - // 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.setObject(13, baos.toByteArray(), java.sql.Types.BLOB); - ps.setString(14, trigger.getName()); - ps.setString(15, trigger.getGroup()); - - insertResult = ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - - 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; - } - - 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()); - ps.setString(4, toBooleanIntString(trigger.isVolatile())); - //ps.setBoolean(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()); - ps.setString(10, toBooleanIntString(job.isStateful())); - ps.setString(11, toBooleanIntString(job.requestsRecovery())); - //ps.setBoolean(10, job.isStateful()); - //ps.setBoolean(11, job.requestsRecovery()); - } else { - ps.setString(8, null); - ps.setString(9, null); - ps.setString(10, "0"); - ps.setString(11, "0"); - //ps.setBoolean(10, false); - //ps.setBoolean(11, false); - } - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - 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)); - ps.setObject(1, baos.toByteArray(), java.sql.Types.BLOB); - //ps.setBytes(1, baos.toByteArray()); - ps.setString(2, job.getName()); - ps.setString(3, job.getGroup()); - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - 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); - ps.setObject(2, baos.toByteArray(), java.sql.Types.BLOB); - //ps.setBytes(2, baos.toByteArray()); - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - 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)); - ps.setString(1, calendarName); - ps.setObject(2, baos.toByteArray(), java.sql.Types.BLOB); - //ps.setBytes(2, baos.toByteArray()); - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - public int deleteVolatileFiredTriggers(Connection conn) throws SQLException { - PreparedStatement ps = null; - try { - ps = conn.prepareStatement(rtp(DELETE_VOLATILE_FIRED_TRIGGERS)); - ps.setString(1, "1"); - //ps.setBoolean(1, true); - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - public Key[] selectVolatileTriggers(Connection conn) throws SQLException { - PreparedStatement ps = null; - ResultSet rs = null; - - try { - ps = conn.prepareStatement(rtp(SELECT_VOLATILE_TRIGGERS)); - ps.setString(1, "1"); - //ps.setBoolean(1, true); - 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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - public Key[] selectVolatileJobs(Connection conn) throws SQLException { - PreparedStatement ps = null; - ResultSet rs = null; - - try { - ps = conn.prepareStatement(rtp(SELECT_VOLATILE_JOBS)); - ps.setString(1, "1"); - //ps.setBoolean(1, true); - rs = ps.executeQuery(); - - ArrayList list = new ArrayList(); - while (rs.next()) { - String triggerName = rs.getString(COL_JOB_NAME); - String groupName = rs.getString(COL_JOB_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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - private static String toBooleanIntString(boolean theBoolean) { - if (String.valueOf(theBoolean).equals("true")) { - return "1"; - } else { - return "0"; - } - } - } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DB2v8Delegate.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DB2v8Delegate.java (.../DB2v8Delegate.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DB2v8Delegate.java (.../DB2v8Delegate.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,536 +16,29 @@ */ package org.quartz.impl.jdbcjobstore; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.math.BigDecimal; -import java.sql.Connection; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Date; -import org.apache.commons.logging.Log; -import org.quartz.Calendar; -import org.quartz.CronTrigger; -import org.quartz.JobDataMap; -import org.quartz.JobDetail; -import org.quartz.Scheduler; -import org.quartz.SimpleTrigger; -import org.quartz.Trigger; -import org.quartz.utils.Key; +import org.quartz.spi.ClassLoadHelper; +import org.slf4j.Logger; /** * Quartz JDBC delegate for DB2 v8 databases. + *

+ * This differs from the StdJDBCDelegate in that it stores + * boolean values in an integer column. + *

* * @author Blair Jensen */ public class DB2v8Delegate extends StdJDBCDelegate { - public DB2v8Delegate(Log logger, String tablePrefix, String instanceId) { - super(logger, tablePrefix, instanceId); + /** + * Sets the designated parameter to the given Java boolean value. + * This translates the boolean to 1/0 for true/false. + */ + @Override + protected void setBoolean(PreparedStatement ps, int index, boolean val) throws SQLException { + ps.setInt(index, ((val) ? 1 : 0)); } - - public DB2v8Delegate(Log log, String tablePrefix, String instanceId, - Boolean useProperties) { - super(log, tablePrefix, instanceId, useProperties); - } - - 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); - ps.setInt(2, 1); - //ps.setBoolean(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); - SimpleTrigger rcvryTrig = new SimpleTrigger("recover_" - + instanceId + "_" + String.valueOf(dumId++), - Scheduler.DEFAULT_RECOVERY_GROUP, new Date(firedTime)); - rcvryTrig.setJobName(jobName); - rcvryTrig.setJobGroup(jobGroup); - rcvryTrig - .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW); - - JobDataMap jd = selectTriggerJobDataMap(conn, trigName, trigGroup); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_NAME", trigName); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_GROUP", trigGroup); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_FIRETIME_IN_MILLISECONDS_AS_STRING", 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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - 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()); - ps.setInt(5, toBooleanInt(job.isDurable())); - ps.setInt(6, toBooleanInt(job.isVolatile())); - ps.setInt(7, toBooleanInt(job.isStateful())); - ps.setInt(8, toBooleanInt(job.requestsRecovery())); - //ps.setBoolean (5, job.isDurable()); - //ps.setBoolean (6, job.isVolatile()); - //ps.setBoolean (7, job.isStateful()); - //ps.setBoolean (8, job.requestsRecovery()); - ps.setBytes(9, baos.toByteArray()); - //ps.setObject(9, baos.toByteArray(), Types.BLOB); - - insertResult = ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - - if (insertResult > 0) { - String[] jobListeners = job.getJobListenerNames(); - for (int i = 0; jobListeners != null && i < jobListeners.length; i++) - insertJobListener(conn, job, jobListeners[i]); - } - - return insertResult; - } - - 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()); - ps.setInt(3, toBooleanInt(job.isDurable())); - ps.setInt(4, toBooleanInt(job.isVolatile())); - ps.setInt(5, toBooleanInt(job.isStateful())); - ps.setInt(6, toBooleanInt(job.requestsRecovery())); - //ps.setBoolean (3, job.isDurable()); - //ps.setBoolean (4, job.isVolatile()); - //ps.setBoolean (5, job.isStateful()); - //ps.setBoolean (6, job.requestsRecovery()); - ps.setBytes (7, baos.toByteArray()); - //ps.setObject(7, baos.toByteArray(), java.sql.Types.BLOB); - ps.setString(8, job.getName()); - ps.setString(9, job.getGroup()); - - insertResult = ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - - 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()); - - 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()); - ps.setInt(5, toBooleanInt(trigger.isVolatile())); - //ps.setBoolean(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) { - ps.setString(10, TTYPE_SIMPLE); - } else if (trigger instanceof CronTrigger) { - ps.setString(10, TTYPE_CRON); - } else { // (trigger instanceof BlobTrigger) - 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.setBytes(15, baos.toByteArray()); - - insertResult = ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - - 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()); - - PreparedStatement ps = null; - - int insertResult = 0; - - try { - ps = conn.prepareStatement(rtp(UPDATE_TRIGGER)); - ps.setString(1, trigger.getJobName()); - ps.setString(2, trigger.getJobGroup()); - ps.setInt(3, toBooleanInt(trigger.isVolatile())); - //ps.setBoolean(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) { - // updateSimpleTrigger(conn, (SimpleTrigger)trigger); - ps.setString(8, TTYPE_SIMPLE); - } else if (trigger instanceof CronTrigger) { - // 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.setBytes (13, baos.toByteArray()); - ps.setString(14, trigger.getName()); - ps.setString(15, trigger.getGroup()); - - insertResult = ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - - 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; - } - - 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()); - ps.setInt(4, toBooleanInt(trigger.isVolatile())); - //ps.setBoolean(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()); - ps.setInt(10, toBooleanInt(job.isStateful())); - ps.setInt(11, toBooleanInt(job.requestsRecovery())); - //ps.setBoolean(10, job.isStateful()); - //ps.setBoolean(11, job.requestsRecovery()); - } else { - ps.setString(8, null); - ps.setString(9, null); - ps.setInt(10, 0); - ps.setInt(11, 0); - //ps.setBoolean(10, false); - //ps.setBoolean(11, false); - } - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - 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)); - ps.setBytes(1, baos.toByteArray()); - //ps.setObject(1, baos.toByteArray(), java.sql.Types.BLOB); - ps.setString(2, job.getName()); - ps.setString(3, job.getGroup()); - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - 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); - ps.setBytes(2, baos.toByteArray()); - //ps.setObject(2, baos.toByteArray(), java.sql.Types.BLOB); - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - 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)); - ps.setBytes(1, baos.toByteArray()); - ps.setString(2, calendarName); - //ps.setObject(2, calendar, java.sql.Types.BLOB); - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - public int deleteVolatileFiredTriggers(Connection conn) throws SQLException { - PreparedStatement ps = null; - try { - ps = conn.prepareStatement(rtp(DELETE_VOLATILE_FIRED_TRIGGERS)); - ps.setInt(1, 1); - //ps.setBoolean(1, true); - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - public Key[] selectVolatileTriggers(Connection conn) throws SQLException { - PreparedStatement ps = null; - ResultSet rs = null; - - try { - ps = conn.prepareStatement(rtp(SELECT_VOLATILE_TRIGGERS)); - ps.setInt(1, 1); // boolean true - //ps.setBoolean(1, true); - 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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - public Key[] selectVolatileJobs(Connection conn) throws SQLException { - PreparedStatement ps = null; - ResultSet rs = null; - - try { - ps = conn.prepareStatement(rtp(SELECT_VOLATILE_JOBS)); - ps.setInt(1, 1); - //ps.setBoolean(1, true); - rs = ps.executeQuery(); - - ArrayList list = new ArrayList(); - while (rs.next()) { - String triggerName = rs.getString(COL_JOB_NAME); - String groupName = rs.getString(COL_JOB_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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - -// private static String toBooleanIntString(boolean theBoolean) { -// if (String.valueOf(theBoolean).equals("true")) { -// return "1"; -// } else { -// return "0"; -// } -// } -// - private static int toBooleanInt(boolean theBoolean) { - if (String.valueOf(theBoolean).equals("true")) { - return 1; - } else { - return 0; - } - } } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DBSemaphore.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DBSemaphore.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DBSemaphore.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,224 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.jdbcjobstore; + +import java.sql.Connection; +import java.util.HashSet; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 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 Logger log = LoggerFactory.getLogger(getClass()); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + ThreadLocal> lockOwners = new ThreadLocal>(); + + private String sql; + private String insertSql; + + private String tablePrefix; + + private String schedName; + + private String expandedSQL; + private String expandedInsertSQL; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public DBSemaphore(String tablePrefix, String schedName, String defaultSQL, String defaultInsertSQL) { + this.tablePrefix = tablePrefix; + this.schedName = schedName; + setSQL(defaultSQL); + setInsertSQL(defaultInsertSQL); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + protected Logger getLog() { + return log; + } + + private HashSet getThreadLocks() { + HashSet threadLocks = 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 theExpandedSQL, String theExpandedInsertSQL) + 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 { + + if(log.isDebugEnabled()) { + log.debug( + "Lock '" + lockName + "' is desired by: " + + Thread.currentThread().getName()); + } + if (!isLockOwner(lockName)) { + + executeSQL(conn, lockName, expandedSQL, expandedInsertSQL); + + 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(String lockName) { + + if (isLockOwner(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(String lockName) { + 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.trim(); + } + + setExpandedSQL(); + } + + protected void setInsertSQL(String insertSql) { + if ((insertSql != null) && (insertSql.trim().length() != 0)) { + this.insertSql = insertSql.trim(); + } + + setExpandedSQL(); + } + + private void setExpandedSQL() { + if (getTablePrefix() != null && getSchedName() != null && sql != null && insertSql != null) { + expandedSQL = Util.rtp(this.sql, getTablePrefix(), getSchedulerNameLiteral()); + expandedInsertSQL = Util.rtp(this.insertSql, getTablePrefix(), getSchedulerNameLiteral()); + } + } + + private String schedNameLiteral = null; + protected String getSchedulerNameLiteral() { + if(schedNameLiteral == null) + schedNameLiteral = "'" + schedName + "'"; + return schedNameLiteral; + } + + public String getSchedName() { + return schedName; + } + + public void setSchedName(String schedName) { + this.schedName = schedName; + + setExpandedSQL(); + } + + protected String getTablePrefix() { + return tablePrefix; + } + + public void setTablePrefix(String tablePrefix) { + this.tablePrefix = tablePrefix; + + setExpandedSQL(); + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DailyTimeIntervalTriggerPersistenceDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DailyTimeIntervalTriggerPersistenceDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DailyTimeIntervalTriggerPersistenceDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,162 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.jdbcjobstore; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.quartz.DailyTimeIntervalScheduleBuilder; +import org.quartz.DailyTimeIntervalTrigger; +import org.quartz.TimeOfDay; +import org.quartz.DateBuilder.IntervalUnit; +import org.quartz.impl.triggers.DailyTimeIntervalTriggerImpl; +import org.quartz.spi.OperableTrigger; + +/** + * Persist a DailyTimeIntervalTrigger by converting internal fields to and from + * SimplePropertiesTriggerProperties. + * + * @see DailyTimeIntervalScheduleBuilder + * @see DailyTimeIntervalTrigger + * + * @since 2.1.0 + * + * @author Zemian Deng + */ +public class DailyTimeIntervalTriggerPersistenceDelegate extends SimplePropertiesTriggerPersistenceDelegateSupport { + + public boolean canHandleTriggerType(OperableTrigger trigger) { + return ((trigger instanceof DailyTimeIntervalTrigger) && !((DailyTimeIntervalTriggerImpl)trigger).hasAdditionalProperties()); + } + + public String getHandledTriggerTypeDiscriminator() { + return TTYPE_DAILY_TIME_INT; + } + + @Override + protected SimplePropertiesTriggerProperties getTriggerProperties(OperableTrigger trigger) { + DailyTimeIntervalTriggerImpl dailyTrigger = (DailyTimeIntervalTriggerImpl)trigger; + SimplePropertiesTriggerProperties props = new SimplePropertiesTriggerProperties(); + + props.setInt1(dailyTrigger.getRepeatInterval()); + props.setString1(dailyTrigger.getRepeatIntervalUnit().name()); + props.setInt2(dailyTrigger.getTimesTriggered()); + + Set days = dailyTrigger.getDaysOfWeek(); + String daysStr = join(days, ","); + props.setString2(daysStr); + + StringBuilder timeOfDayBuffer = new StringBuilder(); + TimeOfDay startTimeOfDay = dailyTrigger.getStartTimeOfDay(); + if (startTimeOfDay != null) { + timeOfDayBuffer.append(startTimeOfDay.getHour()).append(","); + timeOfDayBuffer.append(startTimeOfDay.getMinute()).append(","); + timeOfDayBuffer.append(startTimeOfDay.getSecond()).append(","); + } else { + timeOfDayBuffer.append(",,,"); + } + TimeOfDay endTimeOfDay = dailyTrigger.getEndTimeOfDay(); + if (endTimeOfDay != null) { + timeOfDayBuffer.append(endTimeOfDay.getHour()).append(","); + timeOfDayBuffer.append(endTimeOfDay.getMinute()).append(","); + timeOfDayBuffer.append(endTimeOfDay.getSecond()); + } else { + timeOfDayBuffer.append(",,,"); + } + props.setString3(timeOfDayBuffer.toString()); + + props.setLong1(dailyTrigger.getRepeatCount()); + + return props; + } + + private String join(Set days, String sep) { + StringBuilder sb = new StringBuilder(); + if (days == null || days.size() <= 0) + return ""; + + Iterator itr = days.iterator(); + sb.append(itr.next()); + while(itr.hasNext()) { + sb.append(sep).append(itr.next()); + } + return sb.toString(); + } + + @Override + protected TriggerPropertyBundle getTriggerPropertyBundle(SimplePropertiesTriggerProperties props) { + int repeatCount = (int)props.getLong1(); + int interval = props.getInt1(); + String intervalUnitStr = props.getString1(); + String daysOfWeekStr = props.getString2(); + String timeOfDayStr = props.getString3(); + + IntervalUnit intervalUnit = IntervalUnit.valueOf(intervalUnitStr); + DailyTimeIntervalScheduleBuilder scheduleBuilder = DailyTimeIntervalScheduleBuilder + .dailyTimeIntervalSchedule() + .withInterval(interval, intervalUnit) + .withRepeatCount(repeatCount); + + if (daysOfWeekStr != null) { + Set daysOfWeek = new HashSet(); + String[] nums = daysOfWeekStr.split(","); + if (nums.length > 0) { + for (String num : nums) { + daysOfWeek.add(Integer.parseInt(num)); + } + scheduleBuilder.onDaysOfTheWeek(daysOfWeek); + } + } else { + scheduleBuilder.onDaysOfTheWeek(DailyTimeIntervalScheduleBuilder.ALL_DAYS_OF_THE_WEEK); + } + + if (timeOfDayStr != null) { + String[] nums = timeOfDayStr.split(","); + TimeOfDay startTimeOfDay; + if (nums.length >= 3) { + int hour = Integer.parseInt(nums[0]); + int min = Integer.parseInt(nums[1]); + int sec = Integer.parseInt(nums[2]); + startTimeOfDay = new TimeOfDay(hour, min, sec); + } else { + startTimeOfDay = TimeOfDay.hourMinuteAndSecondOfDay(0, 0, 0); + } + scheduleBuilder.startingDailyAt(startTimeOfDay); + + TimeOfDay endTimeOfDay; + if (nums.length >= 6) { + int hour = Integer.parseInt(nums[3]); + int min = Integer.parseInt(nums[4]); + int sec = Integer.parseInt(nums[5]); + endTimeOfDay = new TimeOfDay(hour, min, sec); + } else { + endTimeOfDay = TimeOfDay.hourMinuteAndSecondOfDay(23, 59, 59); + } + scheduleBuilder.endingDailyAt(endTimeOfDay); + } else { + scheduleBuilder.startingDailyAt(TimeOfDay.hourMinuteAndSecondOfDay(0, 0, 0)); + scheduleBuilder.endingDailyAt(TimeOfDay.hourMinuteAndSecondOfDay(23, 59, 59)); + } + + int timesTriggered = props.getInt2(); + String[] statePropertyNames = { "timesTriggered" }; + Object[] statePropertyValues = { timesTriggered }; + + return new TriggerPropertyBundle(scheduleBuilder, statePropertyNames, statePropertyValues); + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DriverDelegate.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DriverDelegate.java (.../DriverDelegate.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/DriverDelegate.java (.../DriverDelegate.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.io.IOException; @@ -27,14 +24,18 @@ import java.util.Set; import org.quartz.Calendar; -import org.quartz.CronTrigger; +import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; -import org.quartz.SimpleTrigger; +import org.quartz.JobKey; +import org.quartz.JobPersistenceException; import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.quartz.impl.matchers.GroupMatcher; import org.quartz.spi.ClassLoadHelper; +import org.quartz.spi.OperableTrigger; import org.quartz.utils.Key; -import org.quartz.utils.TriggerStatus; +import org.slf4j.Logger; /** *

@@ -67,6 +68,12 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + /** + * @param initString of the format: settingName=settingValue|otherSettingName=otherSettingValue|... + * @throws NoSuchDelegateException + */ + public void initialize(Logger logger, String tablePrefix, String schedName, String instanceId, ClassLoadHelper classLoadHelper, boolean useProperties, String initString) throws NoSuchDelegateException; + //--------------------------------------------------------------------------- // startup / recovery //--------------------------------------------------------------------------- @@ -86,9 +93,9 @@ * the second old state to update * @return number of rows updated */ - public int updateTriggerStatesFromOtherStates(Connection conn, - String newState, String oldState1, String oldState2) - throws SQLException; + int updateTriggerStatesFromOtherStates(Connection conn, + String newState, String oldState1, String oldState2) + throws SQLException; /** *

@@ -101,8 +108,8 @@ * @return an array of {@link * org.quartz.utils.Key} objects */ - public Key[] selectMisfiredTriggers(Connection conn, long ts) - throws SQLException; + List selectMisfiredTriggers(Connection conn, long ts) + throws SQLException; /** *

@@ -115,8 +122,37 @@ * @return an array of {@link * org.quartz.utils.Key} objects */ - public Key[] selectMisfiredTriggersInState(Connection conn, String state, - long ts) throws SQLException; + List 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 org.quartz.utils.Key} objects. Must not be null. + * + * @return Whether there are more misfired triggers left to find beyond + * the given count. + */ + boolean hasMisfiredTriggersInState(Connection conn, String state1, + long ts, int count, List resultList) throws SQLException; + + /** + *

+ * Get the number of triggers in the given state that have + * misfired - according to the given timestamp. + *

+ * + * @param conn the DB Connection + */ + int countMisfiredTriggersInState( + Connection conn, String state1, long ts) throws SQLException; /** *

@@ -129,8 +165,9 @@ * @return an array of {@link * org.quartz.utils.Key} objects */ - public Key[] selectMisfiredTriggersInGroupInState(Connection conn, - String groupName, String state, long ts) throws SQLException; + List selectMisfiredTriggersInGroupInState(Connection conn, + String groupName, String state, long ts) throws SQLException; + /** *

@@ -153,8 +190,8 @@ * the DB Connection * @return an array of {@link org.quartz.Trigger} objects */ - public Trigger[] selectTriggersForRecoveringJobs(Connection conn) - throws SQLException, IOException, ClassNotFoundException; + List selectTriggersForRecoveringJobs(Connection conn) + throws SQLException, IOException, ClassNotFoundException; /** *

@@ -165,7 +202,7 @@ * the DB Connection * @return the number of rows deleted */ - public int deleteFiredTriggers(Connection conn) throws SQLException; + int deleteFiredTriggers(Connection conn) throws SQLException; /** *

@@ -176,44 +213,9 @@ * the DB Connection * @return the number of rows deleted */ - public int deleteFiredTriggers(Connection conn, String instanceId) - throws SQLException; + int deleteFiredTriggers(Connection conn, String instanceId) + throws SQLException; - /** - *

- * Delete all volatile fired triggers. - *

- * - * @param conn - * the DB Connection - * @return the number of rows deleted - */ - public 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 - * org.quartz.utils.Key} objects - */ - public 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 - * org.quartz.utils.Key} objects - */ - public Key[] selectVolatileJobs(Connection conn) throws SQLException; - //--------------------------------------------------------------------------- // jobs //--------------------------------------------------------------------------- @@ -231,8 +233,8 @@ * @throws IOException * if there were problems serializing the JobDataMap */ - public int insertJobDetail(Connection conn, JobDetail job) - throws IOException, SQLException; + int insertJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException; /** *

@@ -247,8 +249,8 @@ * @throws IOException * if there were problems serializing the JobDataMap */ - public int updateJobDetail(Connection conn, JobDetail job) - throws IOException, SQLException; + int updateJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException; /** *

@@ -257,63 +259,36 @@ * * @param conn * the DB Connection - * @param jobName - * the job name - * @param groupName - * the job group + * * @return an array of {@link * org.quartz.utils.Key} objects */ - public Key[] selectTriggerNamesForJob(Connection conn, String jobName, - String groupName) throws SQLException; + List selectTriggerKeysForJob(Connection conn, JobKey jobKey) 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 - */ - public 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 */ - public int deleteJobDetail(Connection conn, String jobName, String groupName) - throws SQLException; + int deleteJobDetail(Connection conn, JobKey jobKey) + throws SQLException; /** *

- * Check whether or not the given job is stateful. + * Check whether or not the given job disallows concurrent execution. *

* * @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 + * + * @return true if the job exists and disallows concurrent execution, false otherwise */ - public boolean isJobStateful(Connection conn, String jobName, - String groupName) throws SQLException; + boolean isJobNonConcurrent(Connection conn, JobKey jobKey) throws SQLException; /** *

@@ -322,14 +297,11 @@ * * @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; + boolean jobExists(Connection conn, JobKey jobKey) + throws SQLException; /** *

@@ -344,62 +316,27 @@ * @throws IOException * if there were problems serializing the JobDataMap */ - public int updateJobData(Connection conn, JobDetail job) - throws IOException, SQLException; + 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 - */ - public 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 of String listener names - */ - public 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 */ - public JobDetail selectJobDetail(Connection conn, String jobName, - String groupName, ClassLoadHelper loadHelper) - throws ClassNotFoundException, IOException, SQLException; + JobDetail selectJobDetail(Connection conn, JobKey jobKey, + ClassLoadHelper loadHelper) + throws ClassNotFoundException, IOException, SQLException; /** *

@@ -410,7 +347,7 @@ * the DB Connection * @return the total number of jobs stored */ - public int selectNumJobs(Connection conn) throws SQLException; + int selectNumJobs(Connection conn) throws SQLException; /** *

@@ -421,7 +358,7 @@ * the DB Connection * @return an array of String group names */ - public String[] selectJobGroups(Connection conn) throws SQLException; + List selectJobGroups(Connection conn) throws SQLException; /** *

@@ -430,12 +367,12 @@ * * @param conn * the DB Connection - * @param groupName - * the group containing the jobs + * @param matcher + * the group matcher to evaluate against the known jobs * @return an array of String job names */ - public String[] selectJobsInGroup(Connection conn, String groupName) - throws SQLException; + Set selectJobsInGroup(Connection conn, GroupMatcher matcher) + throws SQLException; //--------------------------------------------------------------------------- // triggers @@ -454,53 +391,11 @@ * 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; + int insertTrigger(Connection conn, OperableTrigger 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 - */ - public 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 - */ - public 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 - */ - public int insertCronTrigger(Connection conn, CronTrigger trigger) - throws SQLException; - - /** - *

* Update the base trigger data. *

* @@ -512,84 +407,35 @@ * 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; + int updateTrigger(Connection conn, OperableTrigger trigger, String state, + JobDetail jobDetail) throws SQLException, IOException; /** *

- * Update the simple trigger data. + * Check whether or not a trigger exists. *

* * @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; - - /** - *

- * 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; + boolean triggerExists(Connection conn, TriggerKey triggerKey) throws SQLException; /** *

- * 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; - - /** - *

- * 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 - */ - public 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 */ - public int updateTriggerState(Connection conn, String triggerName, - String groupName, String state) throws SQLException; + int updateTriggerState(Connection conn, TriggerKey triggerKey, + String state) throws SQLException; /** *

@@ -599,20 +445,16 @@ * * @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; + int updateTriggerStateFromOtherState(Connection conn, + TriggerKey triggerKey, String newState, String oldState) throws SQLException; /** *

@@ -622,10 +464,7 @@ * * @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 @@ -637,44 +476,21 @@ * @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; + int updateTriggerStateFromOtherStates(Connection conn, + TriggerKey triggerKey, 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 - */ - public 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 matcher + * the group matcher to evaluate against the known triggers * @param newState * the new state for the trigger * @param oldState1 @@ -686,9 +502,9 @@ * @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; + int updateTriggerGroupStateFromOtherStates(Connection conn, + GroupMatcher matcher, String newState, String oldState1, + String oldState2, String oldState3) throws SQLException; /** *

@@ -698,18 +514,18 @@ * * @param conn * the DB connection - * @param groupName - * the group containing the triggers + * @param matcher + * the matcher to evaluate against the known 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; + int updateTriggerGroupStateFromOtherState(Connection conn, + GroupMatcher matcher, String newState, String oldState) + throws SQLException; /** *

@@ -718,16 +534,13 @@ * * @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; + int updateTriggerStatesForJob(Connection conn, JobKey jobKey, + String state) throws SQLException; /** *

@@ -737,147 +550,39 @@ * * @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 */ - public int updateTriggerStatesForJobFromOtherState(Connection conn, - String jobName, String groupName, String state, String oldState) - throws SQLException; + int updateTriggerStatesForJobFromOtherState(Connection conn, + JobKey jobKey, String state, String oldState) + throws SQLException; /** *

- * Delete all of the listeners associated with a given trigger. + * Delete the base trigger data for a 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; - - /** - *

- * 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; - - /** - *

- * 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 of String trigger listener names - */ - public 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 */ - public int deleteSimpleTrigger(Connection conn, String triggerName, - String groupName) throws SQLException; + int deleteTrigger(Connection conn, TriggerKey triggerKey) 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 - */ - public 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 - */ - public 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 - */ - public 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 */ - public int selectNumTriggersForJob(Connection conn, String jobName, - String groupName) throws SQLException; + int selectNumTriggersForJob(Connection conn, JobKey jobKey) throws SQLException; /** *

@@ -886,31 +591,22 @@ * * @param conn * the DB Connection - * @param triggerName - * the name of the trigger - * @param groupName - * the group containing the trigger + * * @return the {@link org.quartz.JobDetail} object * associated with the given trigger */ - public JobDetail selectJobForTrigger(Connection conn, String triggerName, - String groupName, ClassLoadHelper loadHelper) + JobDetail selectJobForTrigger(Connection conn, ClassLoadHelper loadHelper, + TriggerKey triggerKey) throws ClassNotFoundException, SQLException; /** *

- * Select the stateful jobs which are referenced by triggers in the given - * trigger group. + * Select the job to which the trigger is associated. Allow option to load actual job class or not. When case of + * remove, we do not need to load the class, which in many cases, it's no longer exists. *

- * - * @param conn - * the DB Connection - * @param groupName - * the trigger group - * @return a List of Keys to jobs. */ - public List selectStatefulJobsOfTriggerGroup(Connection conn, - String groupName) throws SQLException; + public JobDetail selectJobForTrigger(Connection conn, ClassLoadHelper loadHelper, + TriggerKey triggerKey, boolean loadJobClass) throws ClassNotFoundException, SQLException; /** *

@@ -919,17 +615,14 @@ * * @param conn * the DB Connection - * @param jobName - * the name of the trigger - * @param groupName - * the group containing the trigger + * * @return an array of (@link org.quartz.Trigger) objects * associated with a given job. * @throws SQLException + * @throws JobPersistenceException */ - public Trigger[] selectTriggersForJob(Connection conn, String jobName, - String groupName) throws SQLException, ClassNotFoundException, - IOException; + List selectTriggersForJob(Connection conn, JobKey jobKey) throws SQLException, ClassNotFoundException, + IOException, JobPersistenceException; /** *

@@ -938,32 +631,28 @@ * * @param conn * the DB Connection - * @param jobName - * the name of the trigger - * @param groupName - * the group containing the trigger + * @param calName + * the name of the calendar * @return an array of (@link org.quartz.Trigger) objects - * associated with a given job. + * associated with the given calendar. * @throws SQLException + * @throws JobPersistenceException */ - public Trigger[] selectTriggersForCalendar(Connection conn, String calName) - throws SQLException, ClassNotFoundException, IOException; + List selectTriggersForCalendar(Connection conn, String calName) + throws SQLException, ClassNotFoundException, IOException, JobPersistenceException; /** *

* 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 org.quartz.Trigger} object + * @throws JobPersistenceException */ - public Trigger selectTrigger(Connection conn, String triggerName, - String groupName) throws SQLException, ClassNotFoundException, - IOException; + OperableTrigger selectTrigger(Connection conn, TriggerKey triggerKey) throws SQLException, ClassNotFoundException, + IOException, JobPersistenceException; /** *

@@ -979,9 +668,9 @@ * @return the {@link org.quartz.JobDataMap} of the Trigger, * never null, but possibly empty. */ - public JobDataMap selectTriggerJobDataMap(Connection conn, String triggerName, - String groupName) throws SQLException, ClassNotFoundException, - IOException; + JobDataMap selectTriggerJobDataMap(Connection conn, String triggerName, + String groupName) throws SQLException, ClassNotFoundException, + IOException; /** *

@@ -990,14 +679,10 @@ * * @param conn * the DB Connection - * @param triggerName - * the name of the trigger - * @param groupName - * the group containing the trigger + * * @return the {@link org.quartz.Trigger} object */ - public String selectTriggerState(Connection conn, String triggerName, - String groupName) throws SQLException; + String selectTriggerState(Connection conn, TriggerKey triggerKey) throws SQLException; /** *

@@ -1006,14 +691,11 @@ * * @param conn * the DB Connection - * @param triggerName - * the name of the trigger - * @param groupName - * the group containing the trigger + * * @return a TriggerStatus object, or null */ - public TriggerStatus selectTriggerStatus(Connection conn, - String triggerName, String groupName) throws SQLException; + TriggerStatus selectTriggerStatus(Connection conn, + TriggerKey triggerKey) throws SQLException; /** *

@@ -1024,7 +706,7 @@ * the DB Connection * @return the total number of triggers stored */ - public int selectNumTriggers(Connection conn) throws SQLException; + int selectNumTriggers(Connection conn) throws SQLException; /** *

@@ -1035,21 +717,23 @@ * the DB Connection * @return an array of String group names */ - public String[] selectTriggerGroups(Connection conn) throws SQLException; + List selectTriggerGroups(Connection conn) throws SQLException; + List selectTriggerGroups(Connection conn, GroupMatcher matcher) 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 of String trigger names + * @param matcher + * to evaluate against known triggers + * @return a Set of TriggerKeys */ - public String[] selectTriggersInGroup(Connection conn, String groupName) - throws SQLException; + Set selectTriggersInGroup(Connection conn, GroupMatcher matcher) + throws SQLException; /** *

@@ -1062,26 +746,29 @@ * the state the triggers must be in * @return an array of trigger Key s */ - public Key[] selectTriggersInState(Connection conn, String state) - throws SQLException; + List selectTriggersInState(Connection conn, String state) + throws SQLException; - public int insertPausedTriggerGroup(Connection conn, String groupName) - throws SQLException; + int insertPausedTriggerGroup(Connection conn, String groupName) + throws SQLException; - public int deletePausedTriggerGroup(Connection conn, String groupName) - throws SQLException; + int deletePausedTriggerGroup(Connection conn, String groupName) + throws SQLException; - public int deleteAllPausedTriggerGroups(Connection conn) - throws SQLException; + int deletePausedTriggerGroup(Connection conn, GroupMatcher matcher) + throws SQLException; - public boolean isTriggerGroupPaused(Connection conn, String groupName) - throws SQLException; + int deleteAllPausedTriggerGroups(Connection conn) + throws SQLException; - public Set selectPausedTriggerGroups(Connection conn) + boolean isTriggerGroupPaused(Connection conn, String groupName) throws SQLException; + + Set selectPausedTriggerGroups(Connection conn) + throws SQLException; - public boolean isExistingTriggerGroup(Connection conn, String groupName) - throws SQLException; + boolean isExistingTriggerGroup(Connection conn, String groupName) + throws SQLException; //--------------------------------------------------------------------------- // calendars @@ -1102,8 +789,8 @@ * @throws IOException * if there were problems serializing the calendar */ - public int insertCalendar(Connection conn, String calendarName, - Calendar calendar) throws IOException, SQLException; + int insertCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException; /** *

@@ -1120,8 +807,8 @@ * @throws IOException * if there were problems serializing the calendar */ - public int updateCalendar(Connection conn, String calendarName, - Calendar calendar) throws IOException, SQLException; + int updateCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException; /** *

@@ -1134,8 +821,8 @@ * the name of the calendar * @return true if the trigger exists, false otherwise */ - public boolean calendarExists(Connection conn, String calendarName) - throws SQLException; + boolean calendarExists(Connection conn, String calendarName) + throws SQLException; /** *

@@ -1153,8 +840,8 @@ * @throws IOException * if there were problems deserializing the calendar */ - public Calendar selectCalendar(Connection conn, String calendarName) - throws ClassNotFoundException, IOException, SQLException; + Calendar selectCalendar(Connection conn, String calendarName) + throws ClassNotFoundException, IOException, SQLException; /** *

@@ -1167,8 +854,8 @@ * the name of the calendar * @return true if any triggers reference the calendar, false otherwise */ - public boolean calendarIsReferenced(Connection conn, String calendarName) - throws SQLException; + boolean calendarIsReferenced(Connection conn, String calendarName) + throws SQLException; /** *

@@ -1181,8 +868,8 @@ * the name of the trigger * @return the number of rows deleted */ - public int deleteCalendar(Connection conn, String calendarName) - throws SQLException; + int deleteCalendar(Connection conn, String calendarName) + throws SQLException; /** *

@@ -1193,7 +880,7 @@ * the DB Connection * @return the total number of calendars stored */ - public int selectNumCalendars(Connection conn) throws SQLException; + int selectNumCalendars(Connection conn) throws SQLException; /** *

@@ -1204,7 +891,7 @@ * the DB Connection * @return an array of String calendar names */ - public String[] selectCalendars(Connection conn) throws SQLException; + List selectCalendars(Connection conn) throws SQLException; //--------------------------------------------------------------------------- // trigger firing @@ -1218,8 +905,10 @@ * @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; + long selectNextFireTime(Connection conn) throws SQLException; /** *

@@ -1234,11 +923,51 @@ * 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; + 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 of getNextFireTime() 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. + * + * @deprecated - This remained for compatibility reason. Use {@link #selectTriggerToAcquire(Connection, long, long, int)} instead. + */ + public List selectTriggerToAcquire(Connection conn, long noLaterThan, long noEarlierThan) + 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 of getNextFireTime() of the triggers (exclusive) + * @param noEarlierThan + * highest value of getNextFireTime() of the triggers (inclusive) + * @param maxCount + * maximum number of trigger keys allow to acquired in the returning list. + * + * @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, int maxCount) + throws SQLException; + + /** + *

* Insert a fired trigger. *

* @@ -1250,19 +979,35 @@ * 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 jobDetail) throws SQLException; + int insertFiredTrigger(Connection conn, OperableTrigger trigger, + String state, JobDetail jobDetail) throws SQLException; /** *

+ * Update a fired trigger record. Will update the fields + * "firing instance", "fire time", and "state". + *

+ * + * @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 updateFiredTrigger(Connection conn, OperableTrigger 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. *

* * @return a List of FiredTriggerRecord objects. */ - public List selectFiredTriggerRecords(Connection conn, String triggerName, - String groupName) throws SQLException; + List selectFiredTriggerRecords(Connection conn, String triggerName, String groupName) throws SQLException; /** *

@@ -1272,8 +1017,7 @@ * * @return a List of FiredTriggerRecord objects. */ - public List selectFiredTriggerRecordsByJob(Connection conn, String jobName, - String groupName) throws SQLException; + List selectFiredTriggerRecordsByJob(Connection conn, String jobName, String groupName) throws SQLException; /** *

@@ -1283,11 +1027,27 @@ * * @return a List of FiredTriggerRecord objects. */ - public List selectInstancesFiredTriggerRecords(Connection conn, - String instanceName) throws SQLException; + 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. *

* @@ -1297,8 +1057,8 @@ * the fired trigger entry to delete * @return the number of rows deleted */ - public int deleteFiredTrigger(Connection conn, String entryId) - throws SQLException; + int deleteFiredTrigger(Connection conn, String entryId) + throws SQLException; /** *

@@ -1307,10 +1067,10 @@ * * @param conn * the DB Connection + * * @return the number instances of the identified job currently executing. */ - public int selectJobExecutionCount(Connection conn, String jobName, - String jobGroup) throws SQLException; + int selectJobExecutionCount(Connection conn, JobKey jobKey) throws SQLException; /** *

@@ -1321,9 +1081,9 @@ * the DB Connection * @return the number of inserted rows. */ - public int insertSchedulerState(Connection conn, String instanceId, - long checkInTime, long interval, String recoverer) - throws SQLException; + int insertSchedulerState(Connection conn, String instanceId, + long checkInTime, long interval) + throws SQLException; /** *

@@ -1334,8 +1094,8 @@ * the DB Connection * @return the number of deleted rows. */ - public int deleteSchedulerState(Connection conn, String instanceId) - throws SQLException; + int deleteSchedulerState(Connection conn, String instanceId) + throws SQLException; /** @@ -1347,8 +1107,8 @@ * the DB Connection * @return the number of updated rows. */ - public int updateSchedulerState(Connection conn, String instanceId, long checkInTime, String recoverer) - throws SQLException; + int updateSchedulerState(Connection conn, String instanceId, long checkInTime) + throws SQLException; /** *

@@ -1363,9 +1123,18 @@ * @param conn * the DB Connection */ - public List selectSchedulerStateRecords(Connection conn, String instanceId) // TODO: this method would be more handy if it returned a map. - throws SQLException; + List selectSchedulerStateRecords(Connection conn, String instanceId) + throws SQLException; + /** + * Clear (delete!) all scheduling data - all {@link Job}s, {@link Trigger}s + * {@link Calendar}s. + * + * @throws JobPersistenceException + */ + void clearData(Connection conn) + throws SQLException; + } // EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/FiredTriggerRecord.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/FiredTriggerRecord.java (.../FiredTriggerRecord.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/FiredTriggerRecord.java (.../FiredTriggerRecord.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,12 +15,10 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; -import org.quartz.utils.Key; +import org.quartz.JobKey; +import org.quartz.TriggerKey; /** *

@@ -31,6 +29,8 @@ */ public class FiredTriggerRecord implements java.io.Serializable { + private static final long serialVersionUID = -7183096398865657533L; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -43,20 +43,22 @@ private long fireTimestamp; + private long scheduleTimestamp; + private String schedulerInstanceId; - private Key triggerKey; + private TriggerKey triggerKey; private String fireInstanceState; - private boolean triggerIsVolatile; + private JobKey jobKey; - private Key jobKey; + private boolean jobDisallowsConcurrentExecution; - private boolean jobIsStateful; - private boolean jobRequestsRecovery; + private int priority; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -73,19 +75,23 @@ return fireTimestamp; } - public boolean isJobIsStateful() { - return jobIsStateful; + public long getScheduleTimestamp() { + return scheduleTimestamp; } - public Key getJobKey() { + public boolean isJobDisallowsConcurrentExecution() { + return jobDisallowsConcurrentExecution; + } + + public JobKey getJobKey() { return jobKey; } public String getSchedulerInstanceId() { return schedulerInstanceId; } - public Key getTriggerKey() { + public TriggerKey getTriggerKey() { return triggerKey; } @@ -101,19 +107,23 @@ fireTimestamp = l; } - public void setJobIsStateful(boolean b) { - jobIsStateful = b; + public void setScheduleTimestamp(long l) { + scheduleTimestamp = l; } - public void setJobKey(Key key) { + public void setJobDisallowsConcurrentExecution(boolean b) { + jobDisallowsConcurrentExecution = b; + } + + public void setJobKey(JobKey key) { jobKey = key; } public void setSchedulerInstanceId(String string) { schedulerInstanceId = string; } - public void setTriggerKey(Key key) { + public void setTriggerKey(TriggerKey key) { triggerKey = key; } @@ -125,18 +135,20 @@ 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 Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/HSQLDBDelegate.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/HSQLDBDelegate.java (.../HSQLDBDelegate.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/HSQLDBDelegate.java (.../HSQLDBDelegate.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,7 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ + package org.quartz.impl.jdbcjobstore; import java.io.IOException; @@ -26,7 +24,8 @@ import java.sql.ResultSet; import java.sql.SQLException; -import org.apache.commons.logging.Log; +import org.quartz.spi.ClassLoadHelper; +import org.slf4j.Logger; /** *

@@ -37,37 +36,7 @@ * @author Jeffrey Wescott */ public class HSQLDBDelegate extends StdJDBCDelegate { - /** - *

- * Create new HSQLDBDelegate instance. - *

- * - * @param logger - * 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 logger - * 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 //--------------------------------------------------------------------------- @@ -89,22 +58,30 @@ * @throws IOException * if deserialization causes an error */ + @Override protected Object getObjectFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + throws ClassNotFoundException, IOException, SQLException { InputStream binaryInput = rs.getBinaryStream(colName); - if(binaryInput == null) + if(binaryInput == null || binaryInput.available() == 0) { return null; + } + Object obj = null; + ObjectInputStream in = new ObjectInputStream(binaryInput); - Object obj = in.readObject(); - in.close(); + try { + obj = in.readObject(); + } finally { + in.close(); + } return obj; } - protected Object getJobDetailFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + @Override + protected Object getJobDataFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { if (canUseProperties()) { InputStream binaryInput = rs.getBinaryStream(colName); return binaryInput; Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/InvalidConfigurationException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/InvalidConfigurationException.java (.../InvalidConfigurationException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/InvalidConfigurationException.java (.../InvalidConfigurationException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; /** @@ -30,6 +27,8 @@ */ public class InvalidConfigurationException extends Exception { + private static final long serialVersionUID = 1836325935209404611L; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JTANonClusteredSemaphore.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JTANonClusteredSemaphore.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JTANonClusteredSemaphore.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,302 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides in memory thread/resource locking that is JTA + * {@link javax.transaction.Transaction} aware. + * It is most appropriate for use when using + * {@link 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 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 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 Logger log = LoggerFactory.getLogger(getClass()); + + private String transactionManagerJNDIName = DEFAULT_TRANSACTION_MANANGER_LOCATION; + + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + protected Logger getLog() { + return log; + } + + public void setTransactionManagerJNDIName(String transactionManagerJNDIName) { + this.transactionManagerJNDIName = transactionManagerJNDIName; + } + + private HashSet getThreadLocks() { + HashSet threadLocks = 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(); + + 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(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 + } + } + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JobStoreCMT.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JobStoreCMT.java (.../JobStoreCMT.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JobStoreCMT.java (.../JobStoreCMT.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,27 +15,16 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ + package org.quartz.impl.jdbcjobstore; import java.sql.Connection; import java.sql.SQLException; -import java.util.List; -import java.util.Set; -import org.quartz.Calendar; -import org.quartz.JobDetail; import org.quartz.JobPersistenceException; -import org.quartz.ObjectAlreadyExistsException; import org.quartz.SchedulerConfigException; -import org.quartz.SchedulerException; -import org.quartz.Trigger; -import org.quartz.core.SchedulingContext; import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.SchedulerSignaler; -import org.quartz.spi.TriggerFiredBundle; import org.quartz.utils.DBConnectionManager; /** @@ -132,20 +121,31 @@ } + @Override public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler) throws SchedulerConfigException { - if (nonManagedTxDsName == null) - throw new SchedulerConfigException( - "Non-ManagedTX DataSource name not set!"); + if (nonManagedTxDsName == null) { + throw new SchedulerConfigException( + "Non-ManagedTX DataSource name not set! " + + "If your 'org.quartz.jobStore.dataSource' is XA, then set " + + "'org.quartz.jobStore.nonManagedTXDataSource' to a non-XA "+ + "datasource (for the same DB). " + + "Otherwise, you can set them to be the same."); + } - setUseDBLocks(true); // *must* use DB locks with CMT... + if (getLockHandler() == null) { + // If the user hasn't specified an explicit lock handler, + // then we *must* use DB locks with CMT... + setUseDBLocks(true); + } super.initialize(loadHelper, signaler); getLog().info("JobStoreCMT initialized."); } + @Override public void shutdown() { super.shutdown(); @@ -156,1314 +156,101 @@ getLog().warn("Database connection shutdown unsuccessful.", sqle); } } - - //--------------------------------------------------------------------------- - // JobStoreSupport methods - //--------------------------------------------------------------------------- - - /** - *

- * 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 { + @Override + protected Connection getNonManagedTXConnection() + throws JobPersistenceException { Connection conn = null; - - boolean transOwner = false; - try { - conn = getNonManagedTXConnection(); - - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - recoverJobs(conn); - - conn.commit(); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } catch (Exception e) { - rollbackConnection(conn); - throw new JobPersistenceException("Error recovering jobs: " - + e.getMessage(), e); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } + conn = DBConnectionManager.getInstance().getConnection( + getNonManagedTXDataSource()); + } catch (SQLException sqle) { + throw new JobPersistenceException( + "Failed to obtain DB connection from data source '" + + getNonManagedTXDataSource() + "': " + + sqle.toString(), sqle); + } catch (Throwable e) { + throw new JobPersistenceException( + "Failed to obtain DB connection from data source '" + + getNonManagedTXDataSource() + "': " + + e.toString(), e); } - } - protected void cleanVolatileTriggerAndJobs() throws JobPersistenceException { - Connection conn = null; - - boolean transOwner = false; - - try { - conn = getNonManagedTXConnection(); - - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - cleanVolatileTriggerAndJobs(conn); - - conn.commit(); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } catch (Exception e) { - rollbackConnection(conn); - throw new JobPersistenceException("Error cleaning volatile data: " - + e.getMessage(), e); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } + if (conn == null) { + throw new JobPersistenceException( + "Could not get connection from DataSource '" + + getNonManagedTXDataSource() + "'"); } - } - //--------------------------------------------------------------------------- - // job / trigger storage methods - //--------------------------------------------------------------------------- - - /** - *

- * Store the given {@link org.quartz.JobDetail} and {@link org.quartz.Trigger}. - *

- * - * @param newJob - * The 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 ObjectAlreadyExistsException, - JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; + // Protect connection attributes we might change. + conn = getAttributeRestoringConnection(conn); + + // Set any connection connection attributes we are to override. try { - if(isLockOnInsert()) { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); + if (!isDontSetNonManagedTXConnectionAutoCommitFalse()) { + conn.setAutoCommit(false); } - - 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); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Store the given {@link org.quartz.JobDetail}. - *

- * - * @param newJob - * The 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(SchedulingContext ctxt, JobDetail newJob, - boolean replaceExisting) throws ObjectAlreadyExistsException, - JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - if(isLockOnInsert() || replaceExisting) { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - } - storeJob(conn, ctxt, newJob, replaceExisting); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Remove (delete) the {@link org.quartz.Job} with the given - * name, and any {@link 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. - *

- * - * @param jobName - * The name of the 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) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - return removeJob(conn, ctxt, jobName, groupName, true); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Retrieve the {@link org.quartz.JobDetail} for the given - * {@link org.quartz.Job}. - *

- * - * @param jobName - * The name of the 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) throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return retrieveJob(conn, ctxt, jobName, groupName); - } finally { - closeConnection(conn); - } - } - - /** - *

- * Store the given {@link org.quartz.Trigger}. - *

- * - * @param newTrigger - * The 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(SchedulingContext ctxt, Trigger newTrigger, - boolean replaceExisting) throws ObjectAlreadyExistsException, - JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - if(isLockOnInsert() || replaceExisting) { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; + if (isTxIsolationLevelReadCommitted()) { + conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); } - - storeTrigger(conn, ctxt, newTrigger, null, replaceExisting, - STATE_WAITING, false, false); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Remove (delete) the {@link 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. - *

- * - * @param triggerName - * The name of the 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) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - - return removeTrigger(conn, ctxt, triggerName, groupName); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - - /** - * @see org.quartz.spi.JobStore#replaceTrigger(org.quartz.core.SchedulingContext, java.lang.String, java.lang.String, org.quartz.Trigger) - */ - public boolean replaceTrigger(SchedulingContext ctxt, String triggerName, String groupName, Trigger newTrigger) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - - return replaceTrigger(conn, ctxt, triggerName, groupName, newTrigger); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Retrieve the given {@link org.quartz.Trigger}. - *

- * - * @param triggerName - * The name of the 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) throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return retrieveTrigger(conn, ctxt, triggerName, groupName); - } finally { - closeConnection(conn); - } - } - - /** - *

- * Store the given {@link org.quartz.Calendar}. - *

- * - * @param calName - * The name of the calendar. - * @param calendar - * The 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(SchedulingContext ctxt, String calName, - Calendar calendar, boolean replaceExisting, boolean updateTriggers) - throws ObjectAlreadyExistsException, JobPersistenceException { - Connection conn = getConnection(); - boolean lockOwner = false; - try { - if(isLockOnInsert() || updateTriggers) { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - lockOwner = true; - } + } catch (SQLException sqle) { + getLog().warn("Failed to override connection auto commit/transaction isolation.", sqle); + } catch (Throwable e) { + try { conn.close(); } catch(Throwable tt) {} - storeCalendar(conn, ctxt, calName, calendar, replaceExisting, updateTriggers); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, lockOwner); - } finally { - closeConnection(conn); - } + throw new JobPersistenceException( + "Failure setting up connection.", e); } + + return conn; } - - /** - *

- * Remove (delete) the {@link org.quartz.Calendar} with the - * given name. - *

- * - *

- * If removal of the Calendar would result in - * s pointing to non-existent calendars, then a - * JobPersistenceException will be thrown.

- * * - * @param calName The name of the 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 { - Connection conn = getConnection(); - boolean lockOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_CALENDAR_ACCESS); - lockOwner = true; - - return removeCalendar(conn, ctxt, calName); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, lockOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Retrieve the given {@link org.quartz.Trigger}. - *

- * - * @param calName - * The name of the Calendar to be retrieved. - * @return The desired Calendar, or null if there is no - * match. - */ - public Calendar retrieveCalendar(SchedulingContext ctxt, String calName) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return retrieveCalendar(conn, ctxt, calName); - } finally { - closeConnection(conn); - } - } - - //--------------------------------------------------------------------------- - // informational methods - //--------------------------------------------------------------------------- - - /** - *

- * Get the number of {@link org.quartz.Job} s that are - * stored in the JobStore. - *

- */ - public int getNumberOfJobs(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getNumberOfJobs(conn, ctxt); - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the number of {@link org.quartz.Trigger} s that are - * stored in the JobsStore. - *

- */ - public int getNumberOfTriggers(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getNumberOfTriggers(conn, ctxt); - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the number of {@link org.quartz.Calendar} s that are - * stored in the JobsStore. - *

- */ - public int getNumberOfCalendars(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getNumberOfCalendars(conn, ctxt); - } finally { - closeConnection(conn); - } - } - - public Set getPausedTriggerGroups(SchedulingContext ctxt) - throws JobPersistenceException { - - Connection conn = getConnection(); - try { - // no locks necessary for read... - Set groups = getPausedTriggerGroups(conn, ctxt); - return groups; - } finally { - closeConnection(conn); - } - } /** - *

- * Get the names of all of the {@link org.quartz.Job} s that - * have the given group name. - *

+ * Execute the given callback having optionally acquired the given lock. + * Because CMT assumes that the connection is already part of a managed + * transaction, it does not attempt to commit or rollback the + * enclosing transaction. * - *

- * If there are no jobs in the given group name, the result should be a - * zero-length array (not null). - *

- */ - public String[] getJobNames(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getJobNames(conn, ctxt, groupName); - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the names of all of the {@link org.quartz.Trigger} s - * that have the given group name. - *

+ * @param lockName The name of the lock to acquire, for example + * "TRIGGER_ACCESS". If null, then no lock is acquired, but the + * txCallback is still executed in a transaction. * - *

- * If there are no triggers in the given group name, the result should be a - * zero-length array (not null). - *

+ * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback) + * @see JobStoreTX#executeInLock(String, TransactionCallback) + * @see JobStoreSupport#getNonManagedTXConnection() + * @see JobStoreSupport#getConnection() */ - public String[] getTriggerNames(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getTriggerNames(conn, ctxt, groupName); - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the names of all of the {@link org.quartz.Job} - * groups. - *

- * - *

- * If there are no known group names, the result should be a zero-length - * array (not null). - *

- */ - public String[] getJobGroupNames(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getJobGroupNames(conn, ctxt); - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the names of all of the {@link org.quartz.Trigger} - * groups. - *

- * - *

- * If there are no known group names, the result should be a zero-length - * array (not null). - *

- */ - public String[] getTriggerGroupNames(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getTriggerGroupNames(conn, ctxt); - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the names of all of the {@link 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). - *

- */ - public String[] getCalendarNames(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getCalendarNames(conn, ctxt); - } finally { - closeConnection(conn); - } - } - - /** - *

- * 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) throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getTriggersForJob(conn, ctxt, jobName, groupName); - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the current state of the identified {@link Trigger}. - *

- * - * @see Trigger#STATE_NORMAL - * @see Trigger#STATE_PAUSED - * @see Trigger#STATE_COMPLETE - * @see Trigger#STATE_ERROR - * @see Trigger#STATE_NONE - */ - public int getTriggerState(SchedulingContext ctxt, String triggerName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getTriggerState(conn, ctxt, triggerName, groupName); - } finally { - closeConnection(conn); - } - } - - //--------------------------------------------------------------------------- - // trigger state manipulation methods - //--------------------------------------------------------------------------- - - /** - *

- * Pause the {@link org.quartz.Trigger} with the given name. - *

- * - * @see #resumeTrigger(SchedulingContext, String, String) - */ - public void pauseTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); + @Override + protected Object executeInLock( + String lockName, + TransactionCallback txCallback) throws JobPersistenceException { boolean transOwner = false; + Connection conn = null; try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - pauseTrigger(conn, ctxt, triggerName, groupName); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Pause all of the {@link org.quartz.Trigger}s in the - * given group. - *

- * - * @see #resumeTriggerGroup(SchedulingContext, String) - */ - public void pauseTriggerGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - pauseTriggerGroup(conn, ctxt, groupName); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Pause the {@link org.quartz.Job} with the given name - by - * pausing all of its current Triggers. - *

- * - * @see #resumeJob(SchedulingContext, String, String) - */ - public void pauseJob(SchedulingContext ctxt, String jobName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - Trigger[] triggers = getTriggersForJob(conn, ctxt, jobName, - groupName); - for (int j = 0; j < triggers.length; j++) { - pauseTrigger(conn, ctxt, triggers[j].getName(), triggers[j] - .getGroup()); - } - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Pause all of the {@link org.quartz.Job}s in the given - * group - by pausing all of their Triggers. - *

- * - * @see #resumeJobGroup(SchedulingContext, String) - */ - public void pauseJobGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - String[] jobNames = getJobNames(conn, ctxt, groupName); - - for (int i = 0; i < jobNames.length; i++) { - Trigger[] triggers = getTriggersForJob(conn, ctxt, jobNames[i], - groupName); - for (int j = 0; j < triggers.length; j++) { - pauseTrigger(conn, ctxt, triggers[j].getName(), triggers[j] - .getGroup()); + if (lockName != null) { + // If we aren't using db locks, then delay getting DB connection + // until after acquiring the lock since it isn't needed. + if (getLockHandler().requiresConnection()) { + conn = getConnection(); } + + transOwner = getLockHandler().obtainLock(conn, lockName); } - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - /** - *

- * Resume (un-pause) the {@link 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. - *

- * - * @see #pauseTrigger(SchedulingContext, String, String) - */ - public void resumeTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - resumeTrigger(conn, ctxt, triggerName, groupName); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Resume (un-pause) all of the {@link 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. - *

- * - * @see #pauseTriggerGroup(SchedulingContext, String) - */ - public void resumeTriggerGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - resumeTriggerGroup(conn, ctxt, groupName); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Resume (un-pause) the {@link 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. - *

- * - * @see #pauseJob(SchedulingContext, String, String) - */ - public void resumeJob(SchedulingContext ctxt, String jobName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - Trigger[] triggers = getTriggersForJob(conn, ctxt, jobName, - groupName); - for (int j = 0; j < triggers.length; j++) { - resumeTrigger(conn, ctxt, triggers[j].getName(), triggers[j] - .getGroup()); + if (conn == null) { + conn = getConnection(); } - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - /** - *

- * Resume (un-pause) all of the {@link 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. - *

- * - * @see #pauseJobGroup(SchedulingContext, String) - */ - public void resumeJobGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - String[] jobNames = getJobNames(conn, ctxt, groupName); - - for (int i = 0; i < jobNames.length; i++) { - Trigger[] triggers = getTriggersForJob(conn, ctxt, jobNames[i], - groupName); - for (int j = 0; j < triggers.length; j++) { - resumeTrigger(conn, ctxt, triggers[j].getName(), - triggers[j].getGroup()); - } - } + return txCallback.execute(conn); } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Pause all triggers - equivalent of calling pauseTriggerGroup(group) - * on every group. - *

- * - *

- * When resumeAll() is called (to un-pause), trigger misfire - * instructions WILL be applied. - *

- * - * @see #resumeAll(SchedulingContext) - * @see #pauseTriggerGroup(SchedulingContext, String) - */ - public void pauseAll(SchedulingContext ctxt) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - pauseAll(conn, ctxt); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * 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. - *

- * - * @see #pauseAll(SchedulingContext) - */ - public void resumeAll(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - resumeAll(conn, ctxt); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - //--------------------------------------------------------------------------- - // trigger firing methods - //--------------------------------------------------------------------------- - - /** - *

- * 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) - throws JobPersistenceException { - Connection conn = null; - boolean transOwner = false; - - try { - conn = getNonManagedTXConnection(); - - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - Trigger trigger = acquireNextTrigger(conn, ctxt, noLaterThan); - - conn.commit(); - return trigger; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } catch (Exception e) { - rollbackConnection(conn); - throw new JobPersistenceException( - "Error acquiring next firable trigger: " + e.getMessage(), - e); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Inform the JobStore that the scheduler no longer plans to - * fire the given Trigger, that it had previously acquired - * (reserved). - *

- */ - public void releaseAcquiredTrigger(SchedulingContext ctxt, Trigger trigger) - throws JobPersistenceException { - Connection conn = null; - boolean transOwner = false; - - try { - conn = getNonManagedTXConnection(); - - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - releaseAcquiredTrigger(conn, ctxt, trigger); - conn.commit(); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } catch (Exception e) { - rollbackConnection(conn); - throw new JobPersistenceException( - "Error releasing acquired trigger: " + e.getMessage(), e); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Inform the JobStore that the scheduler is now firing the - * given Trigger (executing its associated Job), - * that it had previously acquired (reserved). - *

- * - * @return null if the trigger or it's job or calendar no longer exist, or - * if the trigger was not successfully put into the 'executing' - * state. - */ - public TriggerFiredBundle triggerFired(SchedulingContext ctxt, - Trigger trigger) throws JobPersistenceException { - Connection conn = null; - boolean transOwner = false; - - try { - conn = getNonManagedTXConnection(); - - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - TriggerFiredBundle tfb = null; - JobPersistenceException err = null; try { - tfb = triggerFired(conn, ctxt, trigger); - } catch (JobPersistenceException jpe) { - if (jpe.getErrorCode() != SchedulerException.ERR_PERSISTENCE_JOB_DOES_NOT_EXIST) - throw jpe; - err = jpe; + releaseLock(LOCK_TRIGGER_ACCESS, transOwner); + } finally { + cleanupConnection(conn); } - - if (err != null) throw err; - - conn.commit(); - return tfb; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } catch (Exception e) { - rollbackConnection(conn); - throw new JobPersistenceException("TX failure: " + e.getMessage(), - e); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } } } - - /** - *

- * Inform the JobStore that the scheduler has completed the - * firing of the given Trigger (and the execution its - * associated Job), and that the {@link org.quartz.JobDataMap} - * in the given JobDetail should be updated if the Job - * is stateful. - *

- */ - public void triggeredJobComplete(SchedulingContext ctxt, Trigger trigger, - JobDetail jobDetail, int triggerInstCode) - throws JobPersistenceException { - Connection conn = null; - boolean transOwner = false; - - try { - conn = getNonManagedTXConnection(); - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - triggeredJobComplete(conn, ctxt, trigger, jobDetail, - triggerInstCode); - - conn.commit(); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } catch (Exception e) { - rollbackConnection(conn); - throw new JobPersistenceException("TX failure: " + e.getMessage(), - e); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - protected boolean doRecoverMisfires() throws JobPersistenceException { - Connection conn = null; - boolean transOwner = false; - boolean moreToDo = false; - - try { - conn = getNonManagedTXConnection(); - - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - - try { - moreToDo = recoverMisfiredJobs(conn, false); - } catch (Exception e) { - throw new JobPersistenceException(e.getMessage(), e); - } - - conn.commit(); - - return moreToDo; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } catch (Exception e) { - rollbackConnection(conn); - throw new JobPersistenceException("TX failure: " + e.getMessage(), - e); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - - } - - protected boolean doCheckin() throws JobPersistenceException { - Connection conn = null; - - boolean transOwner = false; - boolean transStateOwner = false; - boolean recovered = false; - - try { - conn = getNonManagedTXConnection(); - - // 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) - List failedRecords = (firstCheckIn) ? null : clusterCheckIn(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; - } - } - conn.commit(); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } catch (Exception e) { - rollbackConnection(conn); - throw new JobPersistenceException("TX failure: " + e.getMessage(), - e); - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - try { - releaseLock(conn, LOCK_STATE_ACCESS, transStateOwner); - } finally { - closeConnection(conn); - } - } - } - - firstCheckIn = false; - - return recovered; - } - - //--------------------------------------------------------------------------- - // private helpers - //--------------------------------------------------------------------------- - - - protected Connection getNonManagedTXConnection() - throws JobPersistenceException { - try { - Connection conn = DBConnectionManager.getInstance().getConnection( - getNonManagedTXDataSource()); - - if (conn == null) { throw new SQLException( - "Could not get connection from DataSource '" - + getNonManagedTXDataSource() + "'"); } - - try { - if (!isDontSetNonManagedTXConnectionAutoCommitFalse()) - conn.setAutoCommit(false); - - if (isTxIsolationLevelReadCommitted()) - conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); - } catch (SQLException ingore) { - } catch (Exception e) { - if(conn != null) - try { conn.close(); } catch(Throwable tt) {} - throw new JobPersistenceException( - "Failure setting up connection.", e); - } - - return conn; - } catch (SQLException sqle) { - throw new JobPersistenceException( - "Failed to obtain DB connection from data source '" - + getNonManagedTXDataSource() + "': " - + sqle.toString(), sqle); - } catch (Exception e) { - throw new JobPersistenceException( - "Failed to obtain DB connection from data source '" - + getNonManagedTXDataSource() + "': " - + e.toString(), e, - JobPersistenceException.ERR_PERSISTENCE_CRITICAL_FAILURE); - } - } - } // EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JobStoreSupport.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JobStoreSupport.java (.../JobStoreSupport.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JobStoreSupport.java (.../JobStoreSupport.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,44 +15,52 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import java.util.Iterator; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Set; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.quartz.Calendar; -import org.quartz.CronTrigger; +import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; +import org.quartz.JobKey; import org.quartz.JobPersistenceException; import org.quartz.ObjectAlreadyExistsException; import org.quartz.Scheduler; import org.quartz.SchedulerConfigException; import org.quartz.SchedulerException; import org.quartz.SimpleTrigger; import org.quartz.Trigger; -import org.quartz.core.SchedulingContext; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.Trigger.TriggerState; +import org.quartz.TriggerKey; +import org.quartz.impl.DefaultThreadExecutor; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.impl.matchers.StringMatcher; +import org.quartz.impl.matchers.StringMatcher.StringOperatorName; +import org.quartz.impl.triggers.SimpleTriggerImpl; import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.JobStore; +import org.quartz.spi.OperableTrigger; import org.quartz.spi.SchedulerSignaler; +import org.quartz.spi.ThreadExecutor; import org.quartz.spi.TriggerFiredBundle; +import org.quartz.spi.TriggerFiredResult; import org.quartz.utils.DBConnectionManager; -import org.quartz.utils.Key; -import org.quartz.utils.TriggerStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -73,16 +81,10 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - protected static String LOCK_TRIGGER_ACCESS = "TRIGGER_ACCESS"; + protected static final String LOCK_TRIGGER_ACCESS = "TRIGGER_ACCESS"; - protected static String LOCK_JOB_ACCESS = "JOB_ACCESS"; + protected static final String LOCK_STATE_ACCESS = "STATE_ACCESS"; - protected static String LOCK_CALENDAR_ACCESS = "CALENDAR_ACCESS"; - - protected static String LOCK_STATE_ACCESS = "STATE_ACCESS"; - - protected static String LOCK_MISFIRE_ACCESS = "MISFIRE_ACCESS"; - /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -102,10 +104,13 @@ protected String instanceName; protected String delegateClassName; - protected Class delegateClass = StdJDBCDelegate.class; - protected HashMap calendarCache = new HashMap(); + protected String delegateInitString; + + protected Class delegateClass = StdJDBCDelegate.class; + protected HashMap calendarCache = new HashMap(); + private DriverDelegate delegate; private long misfireThreshold = 60000L; // one minute @@ -130,14 +135,30 @@ private ClassLoadHelper classLoadHelper; - private SchedulerSignaler signaler; + private SchedulerSignaler schedSignaler; protected int maxToRecoverAtATime = 20; private boolean setTxIsolationLevelSequential = false; - private long dbRetryInterval = 10000; + private boolean acquireTriggersWithinLock = false; + private long dbRetryInterval = 15000L; // 15 secs + + private boolean makeThreadsDaemons = false; + + private boolean threadsInheritInitializersClassLoadContext = false; + private ClassLoader initializersLoader = null; + + private boolean doubleCheckLockMisfireHandler = true; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private ThreadExecutor threadExecutor = new DefaultThreadExecutor(); + + private volatile boolean schedulerRunning = false; + private volatile boolean shutdown = false; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -172,7 +193,9 @@ *

*/ public void setTablePrefix(String prefix) { - if (prefix == null) prefix = ""; + if (prefix == null) { + prefix = ""; + } this.tablePrefix = prefix; } @@ -191,10 +214,13 @@ * Set whether String-only properties will be handled in JobDataMaps. *

*/ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setUseProperties(String useProp) { - if (useProp == null) useProp = "false"; + if (useProp == null) { + useProp = "false"; + } - this.useProperties = Boolean.valueOf(useProp).booleanValue(); + this.useProperties = Boolean.valueOf(useProp); } /** @@ -226,29 +252,43 @@ } /** - *

- * Set the instance Id of the Scheduler (must be unique within a cluster). - *

+ * Set the instance name of the Scheduler (must be unique within this server instance). */ public void setInstanceName(String instanceName) { this.instanceName = instanceName; } + public void setThreadPoolSize(final int poolSize) { + // + } + + public void setThreadExecutor(ThreadExecutor threadExecutor) { + this.threadExecutor = threadExecutor; + } + + public ThreadExecutor getThreadExecutor() { + return threadExecutor; + } + + /** - *

- * Get the instance Id of the Scheduler (must be unique within a cluster). - *

+ * Get the instance name of the Scheduler (must be unique within this server instance). */ public String getInstanceName() { return instanceName; } + public long getEstimatedTimeToReleaseAndAcquireTrigger() { + return 70; + } + /** *

* Set whether this instance is part of a cluster. *

*/ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setIsClustered(boolean isClustered) { this.isClustered = isClustered; } @@ -280,6 +320,7 @@ * detecting failed instances. *

*/ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setClusterCheckinInterval(long l) { clusterCheckinInterval = l; } @@ -302,6 +343,7 @@ * default is 20. *

*/ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setMaxMisfiresToHandleAtATime(int maxToRecoverAtATime) { this.maxToRecoverAtATime = maxToRecoverAtATime; } @@ -345,16 +387,21 @@ /** * Whether or not to obtain locks when inserting new jobs/triggers. - * Defaults to true, which is safest - some db's (such as + *

+ * Defaults to true, which is safest. Some databases (such as * MS SQLServer) seem to require this to avoid deadlocks under high load, - * while others seem to do fine without. + * while others seem to do fine without. Settings this to false means + * isolation guarantees between job scheduling and trigger acquisition are + * entirely enforced by the database. Depending on the database and it's + * configuration this may cause unusual scheduling behaviors. * *

Setting this property to false will provide a * significant performance increase during the addition of new jobs * and triggers.

* - * @param lockOnInsert + * @param lockOnInsert whether locking should be used when inserting new jobs/triggers */ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setLockOnInsert(boolean lockOnInsert) { this.lockOnInsert = lockOnInsert; } @@ -368,12 +415,14 @@ * next-fire-time, in order for it to be considered "misfired" and thus * have its misfire instruction applied. * - * @param misfireThreshold + * @param misfireThreshold the misfire threshold to use, in millis */ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setMisfireThreshold(long misfireThreshold) { - if (misfireThreshold < 1) - throw new IllegalArgumentException( - "Misfirethreashold must be larger than 0"); + if (misfireThreshold < 1) { + throw new IllegalArgumentException( + "Misfirethreshold must be larger than 0"); + } this.misfireThreshold = misfireThreshold; } @@ -383,28 +432,56 @@ /** * Don't call set autocommit(false) on connections obtained from the - * DataSource. This can be helpfull in a few situations, such as if you + * DataSource. This can be helpful in a few situations, such as if you * have a driver that complains if it is called when it is already off. * - * @param b + * @param b whether or not autocommit should be set to false on db connections */ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setDontSetAutoCommitFalse(boolean b) { dontSetAutoCommitFalse = b; } public boolean isTxIsolationLevelSerializable() { - return setTxIsolationLevelSequential; + return setTxIsolationLevelSequential; } /** * Set the transaction isolation level of DB connections to sequential. * - * @param b + * @param b whether isolation level should be set to sequential. */ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setTxIsolationLevelSerializable(boolean b) { - setTxIsolationLevelSequential = b; + setTxIsolationLevelSequential = b; } + /** + * Whether or not the query and update to acquire a Trigger for firing + * should be performed after obtaining an explicit DB lock (to avoid + * possible race conditions on the trigger's db row). This is the + * behavior prior to Quartz 1.6.3, but is considered unnecessary for most + * databases (due to the nature of the SQL update that is performed), + * and therefore a superfluous performance hit. + */ + public boolean isAcquireTriggersWithinLock() { + return acquireTriggersWithinLock; + } + + /** + * Whether or not the query and update to acquire a Trigger for firing + * should be performed after obtaining an explicit DB lock. This is the + * behavior prior to Quartz 1.6.3, but is considered unnecessary for most + * databases, and therefore a superfluous performance hit. + * + * However, if batch acquisition is used, it is important for this behavior + * to be used for all dbs. + */ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ + public void setAcquireTriggersWithinLock(boolean acquireTriggersWithinLock) { + this.acquireTriggersWithinLock = acquireTriggersWithinLock; + } + /** *

@@ -414,9 +491,12 @@ * @param delegateClassName * the delegate class name */ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ public void setDriverDelegateClass(String delegateClassName) - throws InvalidConfigurationException { - this.delegateClassName = delegateClassName; + throws InvalidConfigurationException { + synchronized(this) { + this.delegateClassName = delegateClassName; + } } /** @@ -430,6 +510,31 @@ return delegateClassName; } + /** + *

+ * Set the JDBC driver delegate's initialization string. + *

+ * + * @param delegateInitString + * the delegate init string + */ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ + public void setDriverDelegateInitString(String delegateInitString) + throws InvalidConfigurationException { + this.delegateInitString = delegateInitString; + } + + /** + *

+ * Get the JDBC driver delegate's initialization string. + *

+ * + * @return the delegate init string + */ + public String getDriverDelegateInitString() { + return delegateInitString; + } + public String getSelectWithLockSQL() { return selectWithLockSQL; } @@ -450,62 +555,137 @@ return classLoadHelper; } + /** + * Get 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 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) + */ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ + 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. + */ + @SuppressWarnings("UnusedDeclaration") /* called reflectively */ + public void setDoubleCheckLockMisfireHandler( + boolean doubleCheckLockMisfireHandler) { + this.doubleCheckLockMisfireHandler = doubleCheckLockMisfireHandler; + } + //--------------------------------------------------------------------------- // interface methods //--------------------------------------------------------------------------- - Log getLog() { - return LogFactory.getLog(getClass()); + protected Logger getLog() { + return log; } /** *

* Called by the QuartzScheduler before the JobStore is - * used, in order to give the it a chance to initialize. + * used, in order to give it a chance to initialize. *

*/ public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler) throws SchedulerConfigException { - if (dsName == null) { throw new SchedulerConfigException( - "DataSource name not set."); } + if (dsName == null) { + throw new SchedulerConfigException("DataSource name not set."); + } classLoadHelper = loadHelper; - this.signaler = signaler; - - if (!getUseDBLocks() && !isClustered()) { - getLog() - .info( - "Using thread monitor-based data access locking (synchronization)."); - lockHandler = new SimpleSemaphore(); - } else { - getLog() - .info( - "Using db table-based data access locking (synchronization)."); - lockHandler = new StdRowLockSemaphore(getTablePrefix(), - getSelectWithLockSQL()); + if(isThreadsInheritInitializersClassLoadContext()) { + log.info("JDBCJobStore threads will inherit ContextClassLoader of thread: " + Thread.currentThread().getName()); + initializersLoader = Thread.currentThread().getContextClassLoader(); } + + this.schedSignaler = signaler; - if (!isClustered()) { - try { - cleanVolatileTriggerAndJobs(); - } catch (SchedulerException se) { - throw new SchedulerConfigException( - "Failure occured during job recovery.", se); + // If the user hasn't specified an explicit lock handler, then + // choose one based on CMT/Clustered/UseDBLocks. + if (getLockHandler() == null) { + + // If the user hasn't specified an explicit lock handler, + // then we *must* use DB locks with clustering + if (isClustered()) { + setUseDBLocks(true); } + + if (getUseDBLocks()) { + if(getDriverDelegateClass() != null && getDriverDelegateClass().equals(MSSQLDelegate.class.getName())) { + if(getSelectWithLockSQL() == null) { + String msSqlDflt = "SELECT * FROM {0}LOCKS WITH (UPDLOCK,ROWLOCK) WHERE " + COL_SCHEDULER_NAME + " = {1} AND LOCK_NAME = ?"; + getLog().info("Detected usage of MSSQLDelegate class - defaulting 'selectWithLockSQL' to '" + msSqlDflt + "'."); + setSelectWithLockSQL(msSqlDflt); + } + } + getLog().info("Using db table-based data access locking (synchronization)."); + setLockHandler(new StdRowLockSemaphore(getTablePrefix(), getInstanceName(), getSelectWithLockSQL())); + } else { + getLog().info( + "Using thread monitor-based data access locking (synchronization)."); + setLockHandler(new SimpleSemaphore()); + } } - } + } + /** * @see org.quartz.spi.JobStore#schedulerStarted() */ public void schedulerStarted() throws SchedulerException { if (isClustered()) { - clusterManagementThread = new ClusterManager(this); + clusterManagementThread = new ClusterManager(); + if(initializersLoader != null) + clusterManagementThread.setContextClassLoader(initializersLoader); clusterManagementThread.initialize(); - } - else { + } else { try { recoverJobs(); } catch (SchedulerException se) { @@ -514,10 +694,23 @@ } } - misfireHandler = new MisfireHandler(this); + misfireHandler = new MisfireHandler(); + if(initializersLoader != null) + misfireHandler.setContextClassLoader(initializersLoader); misfireHandler.initialize(); + schedulerRunning = true; + + getLog().debug("JobStore background threads started (as scheduler was started)."); } + public void schedulerPaused() { + schedulerRunning = false; + } + + public void schedulerResumed() { + schedulerRunning = true; + } + /** *

* Called by the QuartzScheduler to inform the JobStore that @@ -526,16 +719,31 @@ *

*/ public void shutdown() { - if (clusterManagementThread != null) - clusterManagementThread.shutdown(); - - if (misfireHandler != null) misfireHandler.shutdown(); + shutdown = true; + if (misfireHandler != null) { + misfireHandler.shutdown(); + try { + misfireHandler.join(); + } catch (InterruptedException ignore) { + } + } + + if (clusterManagementThread != null) { + clusterManagementThread.shutdown(); + try { + clusterManagementThread.join(); + } catch (InterruptedException ignore) { + } + } + try { DBConnectionManager.getInstance().shutdown(getDataSource()); } catch (SQLException sqle) { getLog().warn("Database connection shutdown unsuccessful.", sqle); } + + getLog().debug("JobStore background threads shutdown."); } public boolean supportsPersistence() { @@ -546,115 +754,92 @@ // helper methods for subclasses //--------------------------------------------------------------------------- - + protected abstract Connection getNonManagedTXConnection() + throws JobPersistenceException; + /** + * Wrap the given 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; try { - Connection conn = DBConnectionManager.getInstance().getConnection( + conn = DBConnectionManager.getInstance().getConnection( getDataSource()); - - if (conn == null) { throw new SQLException( - "Could not get connection from DataSource '" - + getDataSource() + "'"); } - - try { - if (!isDontSetAutoCommitFalse()) conn.setAutoCommit(false); - - if(isTxIsolationLevelSerializable()) - conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); - } catch (SQLException ingore) { - } catch (Exception e) { - if(conn != null) - try { conn.close(); } catch(Throwable tt) {} - throw new JobPersistenceException( - "Failure setting up connection.", e); - } - - return conn; } catch (SQLException sqle) { throw new JobPersistenceException( "Failed to obtain DB connection from data source '" + getDataSource() + "': " + sqle.toString(), sqle); - } catch (Exception e) { + } catch (Throwable e) { throw new JobPersistenceException( "Failed to obtain DB connection from data source '" - + getDataSource() + "': " + e.toString(), e, - JobPersistenceException.ERR_PERSISTENCE_CRITICAL_FAILURE); + + getDataSource() + "': " + e.toString(), e); } - } - - 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); + + if (conn == null) { + throw new JobPersistenceException( + "Could not get connection from DataSource '" + + getDataSource() + "'"); } - } - } - - /** - *

- * Removes all volatile data - *

- * - * @throws JobPersistenceException - * if jobs could not be recovered - */ - protected abstract void cleanVolatileTriggerAndJobs() - throws JobPersistenceException; - /** - *

- * 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); + // Protect connection attributes we might change. + conn = getAttributeRestoringConnection(conn); - for (int i = 0; i < volatileTriggers.length; i++) { - removeTrigger(conn, null, volatileTriggers[i].getName(), - volatileTriggers[i].getGroup()); + // Set any connection connection attributes we are to override. + try { + if (!isDontSetAutoCommitFalse()) { + conn.setAutoCommit(false); } - 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); + if(isTxIsolationLevelSerializable()) { + conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); } - getLog().info( - "Removed " + volatileJobs.length + " Volatile Job(s)."); + } catch (SQLException sqle) { + getLog().warn("Failed to override connection auto commit/transaction isolation.", sqle); + } catch (Throwable e) { + try { conn.close(); } catch(Throwable ignored) {} + + throw new JobPersistenceException( + "Failure setting up connection.", e); + } + + return conn; + } - // clean up any fired trigger entries - getDelegate().deleteVolatileFiredTriggers(conn); - - } catch (Exception e) { - throw new JobPersistenceException("Couldn't clean volatile data: " - + e.getMessage(), e); + protected void releaseLock(String lockName, boolean doIt) { + if (doIt) { + try { + getLockHandler().releaseLock(lockName); + } catch (LockException le) { + getLog().error("Error returning lock: " + le.getMessage(), le); + } } } /** - *

- * Will recover any failed or misfired jobs and clean up the data store as + * Recover any failed or misfired jobs and clean up the data store as * appropriate. - *

* - * @throws JobPersistenceException - * if jobs could not be recovered + * @throws JobPersistenceException if jobs could not be recovered */ - protected abstract void recoverJobs() throws JobPersistenceException; - + protected void recoverJobs() throws JobPersistenceException { + executeInNonManagedTXLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + recoverJobs(conn); + } + }, null); + } + /** *

* Will recover any failed or misfired jobs and clean up the data store as @@ -678,41 +863,39 @@ + " triggers from 'acquired' / 'blocked' state."); // clean up misfired jobs - getDelegate().updateTriggerStateFromOtherStatesBeforeTime(conn, - STATE_MISFIRED, STATE_WAITING, STATE_WAITING, - getMisfireTime()); // only waiting recoverMisfiredJobs(conn, true); // recover jobs marked for recovery that were not fully executed - Trigger[] recoveringJobTriggers = getDelegate() + List recoveringJobTriggers = getDelegate() .selectTriggersForRecoveringJobs(conn); getLog() .info( "Recovering " - + recoveringJobTriggers.length + + recoveringJobTriggers.size() + " 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, + for (OperableTrigger recoveringJobTrigger: recoveringJobTriggers) { + if (jobExists(conn, recoveringJobTrigger.getJobKey())) { + recoveringJobTrigger.computeFirstFireTime(null); + storeTrigger(conn, recoveringJobTrigger, 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()); + List cts = getDelegate().selectTriggersInState(conn, STATE_COMPLETE); + for(TriggerKey ct: cts) { + removeTrigger(conn, ct); + } getLog().info( - "Removed " + ct.length - + " 'complete' triggers."); + "Removed " + cts.size() + " '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); @@ -721,131 +904,209 @@ protected long getMisfireTime() { long misfireTime = System.currentTimeMillis(); - if (getMisfireThreshold() > 0) misfireTime -= getMisfireThreshold(); + if (getMisfireThreshold() > 0) { + misfireTime -= getMisfireThreshold(); + } - return misfireTime; + return (misfireTime > 0) ? misfireTime : 0; } - private int lastRecoverCount = 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 { - protected boolean recoverMisfiredJobs(Connection conn, boolean recovering) - throws JobPersistenceException, NoSuchDelegateException, - SQLException, ClassNotFoundException, IOException { + // If recovering, we want to handle all of the misfired + // triggers right away. + int maxMisfiresToHandleAtATime = + (recovering) ? -1 : getMaxMisfiresToHandleAtATime(); + + List misfiredTriggers = new LinkedList(); + 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().hasMisfiredTriggersInState( + conn, STATE_WAITING, getMisfireTime(), + maxMisfiresToHandleAtATime, misfiredTriggers); - Key[] misfiredTriggers = getDelegate().selectTriggersInState(conn, - STATE_MISFIRED); - - if (misfiredTriggers.length > 0 - && misfiredTriggers.length > getMaxMisfiresToHandleAtATime()) getLog() - .info( - "Handling " - + getMaxMisfiresToHandleAtATime() - + " of " - + misfiredTriggers.length - + " triggers that missed their scheduled fire-time."); - else if (misfiredTriggers.length > 0) getLog().info( - "Handling " + misfiredTriggers.length - + " triggers that missed their scheduled fire-time."); - else + 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."); + "Found 0 triggers that missed their scheduled fire-time."); + return RecoverMisfiredJobsResult.NO_OP; + } - lastRecoverCount = misfiredTriggers.length; + for (TriggerKey triggerKey: misfiredTriggers) { + + OperableTrigger trig = + retrieveTrigger(conn, triggerKey); - for (int i = 0; i < misfiredTriggers.length && i < getMaxMisfiresToHandleAtATime(); i++) { - Trigger trig = getDelegate().selectTrigger(conn, - misfiredTriggers[i].getName(), - misfiredTriggers[i].getGroup()); - - if (trig == null) continue; - - Calendar cal = null; - if (trig.getCalendarName() != null) - cal = retrieveCalendar(conn, null, trig.getCalendarName()); - - String[] listeners = getDelegate().selectTriggerListeners(conn, - trig.getName(), trig.getGroup()); - for (int l = 0; l < listeners.length; ++l) { - trig.addTriggerListener(listeners[l]); + if (trig == null) { + continue; } - - signaler.notifyTriggerListenersMisfired(trig); - trig.updateAfterMisfire(cal); + doUpdateOfMisfiredTrigger(conn, trig, false, STATE_WAITING, recovering); - if (trig.getNextFireTime() == null) - storeTrigger(conn, null, trig, null, true, STATE_COMPLETE, - false, recovering); - else - storeTrigger(conn, null, trig, null, true, STATE_WAITING, - false, recovering); + if(trig.getNextFireTime() != null && trig.getNextFireTime().getTime() < earliestNewTime) + earliestNewTime = trig.getNextFireTime().getTime(); } - if (misfiredTriggers.length > getMaxMisfiresToHandleAtATime()) return true; - - return false; + 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 { + TriggerKey triggerKey, String newStateIfNotComplete, boolean forceState) + throws JobPersistenceException { try { - Trigger trig = getDelegate().selectTrigger(conn, triggerName, - groupName); + OperableTrigger trig = retrieveTrigger(conn, triggerKey); long misfireTime = System.currentTimeMillis(); - if (getMisfireThreshold() > 0) - misfireTime -= getMisfireThreshold(); + if (getMisfireThreshold() > 0) { + misfireTime -= getMisfireThreshold(); + } - if (trig.getNextFireTime().getTime() > misfireTime) return false; + if (trig.getNextFireTime().getTime() > misfireTime) { + return false; + } - Calendar cal = null; - if (trig.getCalendarName() != null) - cal = retrieveCalendar(conn, ctxt, trig.getCalendarName()); + doUpdateOfMisfiredTrigger(conn, trig, forceState, newStateIfNotComplete, false); - signaler.notifyTriggerListenersMisfired(trig); - - trig.updateAfterMisfire(cal); - - if (trig.getNextFireTime() == null) storeTrigger(conn, ctxt, trig, - null, true, STATE_COMPLETE, forceState, false); - else { - storeTrigger(conn, ctxt, trig, null, true, newStateIfNotComplete, - forceState, false); - } - return true; } catch (Exception e) { throw new JobPersistenceException( - "Couldn't update misfired trigger '" + groupName + "." - + triggerName + "': " + e.getMessage(), e); + "Couldn't update misfired trigger '" + triggerKey + "': " + e.getMessage(), e); } } + private void doUpdateOfMisfiredTrigger(Connection conn, OperableTrigger trig, boolean forceState, String newStateIfNotComplete, boolean recovering) throws JobPersistenceException { + Calendar cal = null; + if (trig.getCalendarName() != null) { + cal = retrieveCalendar(conn, trig.getCalendarName()); + } + + schedSignaler.notifyTriggerListenersMisfired(trig); + + trig.updateAfterMisfire(cal); + + if (trig.getNextFireTime() == null) { + storeTrigger(conn, trig, + null, true, STATE_COMPLETE, forceState, recovering); + schedSignaler.notifySchedulerListenersFinalized(trig); + } else { + storeTrigger(conn, trig, null, true, newStateIfNotComplete, + forceState, false); + } + } + /** *

+ * Store the given {@link org.quartz.JobDetail} and {@link org.quartz.Trigger}. + *

+ * + * @param newJob + * The 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 JobDetail newJob, + final OperableTrigger newTrigger) + throws JobPersistenceException { + executeInLock( + (isLockOnInsert()) ? LOCK_TRIGGER_ACCESS : null, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + storeJob(conn, newJob, false); + storeTrigger(conn, newTrigger, newJob, false, + Constants.STATE_WAITING, false, false); + } + }); + } + + /** + *

+ * Store the given {@link org.quartz.JobDetail}. + *

+ * + * @param newJob + * The 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 JobDetail newJob, + final boolean replaceExisting) throws JobPersistenceException { + executeInLock( + (isLockOnInsert() || replaceExisting) ? LOCK_TRIGGER_ACCESS : null, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + storeJob(conn, newJob, replaceExisting); + } + }); + } + + /** + *

* Insert or update a job. *

*/ - protected void storeJob(Connection conn, SchedulingContext ctxt, + protected void storeJob(Connection conn, JobDetail newJob, boolean replaceExisting) - throws ObjectAlreadyExistsException, JobPersistenceException { - if (newJob.isVolatile() && isClustered()) - getLog() - .info( - "note: volatile jobs are effectively non-volatile in a clustered environment."); + throws JobPersistenceException { - boolean existingJob = jobExists(conn, newJob.getName(), newJob - .getGroup()); + boolean existingJob = jobExists(conn, newJob.getKey()); try { if (existingJob) { - if (!replaceExisting) { throw new ObjectAlreadyExistsException( - newJob); } + if (!replaceExisting) { + throw new ObjectAlreadyExistsException(newJob); + } getDelegate().updateJobDetail(conn, newJob); } else { getDelegate().insertJobDetail(conn, newJob); @@ -864,105 +1125,103 @@ * Check existence of a given job. *

*/ - protected boolean jobExists(Connection conn, String jobName, - String groupName) throws JobPersistenceException { + protected boolean jobExists(Connection conn, JobKey jobKey) throws JobPersistenceException { try { - return getDelegate().jobExists(conn, jobName, groupName); + return getDelegate().jobExists(conn, jobKey); } catch (SQLException e) { throw new JobPersistenceException( - "Couldn't determine job existence (" + groupName + "." - + jobName + "): " + e.getMessage(), e); + "Couldn't determine job existence (" + jobKey + "): " + e.getMessage(), e); } } + /** *

+ * Store the given {@link org.quartz.Trigger}. + *

+ * + * @param newTrigger + * The 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 OperableTrigger newTrigger, + final boolean replaceExisting) throws JobPersistenceException { + executeInLock( + (isLockOnInsert() || replaceExisting) ? LOCK_TRIGGER_ACCESS : null, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + storeTrigger(conn, 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, + @SuppressWarnings("ConstantConditions") + protected void storeTrigger(Connection conn, + OperableTrigger 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."); + throws JobPersistenceException { - boolean existingTrigger = triggerExists(conn, newTrigger.getName(), - newTrigger.getGroup()); + boolean existingTrigger = triggerExists(conn, newTrigger.getKey()); + if ((existingTrigger) && (!replaceExisting)) { + throw new ObjectAlreadyExistsException(newTrigger); + } + try { - boolean shouldBepaused = false; + boolean shouldBepaused; if (!forceState) { shouldBepaused = getDelegate().isTriggerGroupPaused( - conn, newTrigger.getGroup()); + conn, newTrigger.getKey().getGroup()); if(!shouldBepaused) { shouldBepaused = getDelegate().isTriggerGroupPaused(conn, ALL_GROUPS_PAUSED); - if (shouldBepaused) - getDelegate().insertPausedTriggerGroup(conn, - newTrigger.getGroup()); + if (shouldBepaused) { + getDelegate().insertPausedTriggerGroup(conn, newTrigger.getKey().getGroup()); + } } - if (shouldBepaused && (state.equals(STATE_WAITING) || state.equals(STATE_ACQUIRED))) - state = STATE_PAUSED; + 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()); + job = getDelegate().selectJobDetail(conn, newTrigger.getJobKey(), 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 == null) { + throw new JobPersistenceException("The job (" + + newTrigger.getJobKey() + + ") referenced by the trigger does not exist."); + } - if (job.isStateful() && !recovering) { - String bstate = getNewStatusForTrigger(conn, ctxt, job.getName(), job - .getGroup()); - if(STATE_BLOCKED.equals(bstate) && STATE_WAITING.equals(state)) - state = STATE_BLOCKED; - if(STATE_BLOCKED.equals(bstate) && STATE_PAUSED.equals(state)) - state = STATE_PAUSED_BLOCKED; + if (job.isConcurrentExectionDisallowed() && !recovering) { + state = checkBlockedState(conn, job.getKey(), state); } + if (existingTrigger) { - if (!replaceExisting) { throw new ObjectAlreadyExistsException( - newTrigger); } - if (newTrigger instanceof SimpleTrigger) { - getDelegate().updateSimpleTrigger(conn, - (SimpleTrigger) newTrigger); - } else if (newTrigger instanceof CronTrigger) { - 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) { - getDelegate().insertSimpleTrigger(conn, - (SimpleTrigger) newTrigger); - } else if (newTrigger instanceof CronTrigger) { - getDelegate().insertCronTrigger(conn, - (CronTrigger) newTrigger); - } else { - getDelegate().insertBlobTrigger(conn, newTrigger); - } } } catch (Exception e) { - throw new JobPersistenceException("Couldn't store trigger: " - + e.getMessage(), e); + throw new JobPersistenceException("Couldn't store trigger '" + newTrigger.getKey() + "' for '" + + newTrigger.getJobKey() + "' job:" + e.getMessage(), e); } } @@ -971,99 +1230,224 @@ * Check existence of a given trigger. *

*/ - protected boolean triggerExists(Connection conn, String triggerName, - String groupName) throws JobPersistenceException { + protected boolean triggerExists(Connection conn, TriggerKey key) throws JobPersistenceException { try { - return getDelegate().triggerExists(conn, triggerName, groupName); + return getDelegate().triggerExists(conn, key); } catch (SQLException e) { throw new JobPersistenceException( - "Couldn't determine trigger existence (" + groupName + "." - + triggerName + "): " + e.getMessage(), e); + "Couldn't determine trigger existence (" + key + "): " + e.getMessage(), e); } } - protected boolean removeJob(Connection conn, SchedulingContext ctxt, - String jobName, String groupName, boolean activeDeleteSafe) - throws JobPersistenceException { + /** + *

+ * Remove (delete) the {@link org.quartz.Job} with the given + * name, and any {@link 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. + *

+ * + * @return true if a Job with the given name & + * group was found and removed from the store. + */ + public boolean removeJob(final JobKey jobKey) throws JobPersistenceException { + return (Boolean) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return removeJob(conn, jobKey) ? + Boolean.TRUE : Boolean.FALSE; + } + }); + } + + protected boolean removeJob(Connection conn, final JobKey jobKey) + throws JobPersistenceException { try { - Key[] jobTriggers = getDelegate().selectTriggerNamesForJob(conn, - jobName, groupName); - for (int i = 0; i < jobTriggers.length; ++i) { - getDelegate().deleteSimpleTrigger(conn, - jobTriggers[i].getName(), jobTriggers[i].getGroup()); - getDelegate().deleteCronTrigger(conn, jobTriggers[i].getName(), - jobTriggers[i].getGroup()); - getDelegate().deleteBlobTrigger(conn, jobTriggers[i].getName(), - jobTriggers[i].getGroup()); - getDelegate().deleteTriggerListeners(conn, - jobTriggers[i].getName(), jobTriggers[i].getGroup()); - getDelegate().deleteTrigger(conn, jobTriggers[i].getName(), - jobTriggers[i].getGroup()); + List jobTriggers = getDelegate().selectTriggerKeysForJob(conn, jobKey); + for (TriggerKey jobTrigger: jobTriggers) { + deleteTriggerAndChildren(conn, jobTrigger); } - getDelegate().deleteJobListeners(conn, jobName, groupName); - - if (getDelegate().deleteJobDetail(conn, jobName, groupName) > 0) { - return true; - } else { - return false; - } + return deleteJobAndChildren(conn, jobKey); } catch (SQLException e) { throw new JobPersistenceException("Couldn't remove job: " + e.getMessage(), e); } } - protected JobDetail retrieveJob(Connection conn, SchedulingContext ctxt, - String jobName, String groupName) throws JobPersistenceException { + public boolean removeJobs(final List jobKeys) throws JobPersistenceException { + + return (Boolean) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + boolean allFound = true; + + // FUTURE_TODO: make this more efficient with a true bulk operation... + for (JobKey jobKey : jobKeys) + allFound = removeJob(conn, jobKey) && allFound; + + return allFound ? Boolean.TRUE : Boolean.FALSE; + } + }); + } + + public boolean removeTriggers(final List triggerKeys) + throws JobPersistenceException { + return (Boolean) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + boolean allFound = true; + + // FUTURE_TODO: make this more efficient with a true bulk operation... + for (TriggerKey triggerKey : triggerKeys) + allFound = removeTrigger(conn, triggerKey) && allFound; + + return allFound ? Boolean.TRUE : Boolean.FALSE; + } + }); + } + + public void storeJobsAndTriggers( + final Map> triggersAndJobs, final boolean replace) + throws JobPersistenceException { + + executeInLock( + (isLockOnInsert() || replace) ? LOCK_TRIGGER_ACCESS : null, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + + // FUTURE_TODO: make this more efficient with a true bulk operation... + for(JobDetail job: triggersAndJobs.keySet()) { + storeJob(conn, job, replace); + for(Trigger trigger: triggersAndJobs.get(job)) { + storeTrigger(conn, (OperableTrigger) trigger, job, replace, + Constants.STATE_WAITING, false, false); + } + } + } + }); + } + + /** + * Delete a job and its listeners. + * + * @see #removeJob(java.sql.Connection, org.quartz.JobKey) + * @see #removeTrigger(Connection, TriggerKey) + */ + private boolean deleteJobAndChildren(Connection conn, JobKey key) + throws NoSuchDelegateException, SQLException { + + return (getDelegate().deleteJobDetail(conn, key) > 0); + } + + /** + * Delete a trigger, its listeners, and its Simple/Cron/BLOB sub-table entry. + * + * @see #removeJob(java.sql.Connection, org.quartz.JobKey) + * @see #removeTrigger(Connection, TriggerKey) + * @see #replaceTrigger(Connection, TriggerKey, OperableTrigger) + */ + private boolean deleteTriggerAndChildren(Connection conn, TriggerKey key) + throws SQLException, NoSuchDelegateException { + + return (getDelegate().deleteTrigger(conn, key) > 0); + } + + /** + *

+ * Retrieve the {@link org.quartz.JobDetail} for the given + * {@link org.quartz.Job}. + *

+ * + * @return The desired Job, or null if there is no match. + */ + public JobDetail retrieveJob(final JobKey jobKey) throws JobPersistenceException { + return (JobDetail)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return retrieveJob(conn, jobKey); + } + }); + } + + protected JobDetail retrieveJob(Connection conn, JobKey key) throws JobPersistenceException { try { - JobDetail job = getDelegate().selectJobDetail(conn, jobName, - groupName, getClassLoadHelper()); - String[] listeners = getDelegate().selectJobListeners(conn, - jobName, groupName); - for (int i = 0; i < listeners.length; ++i) { - job.addJobListener(listeners[i]); - } - return job; + return getDelegate().selectJobDetail(conn, key, + getClassLoadHelper()); } 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); + + e.getMessage(), e); } 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); + + e.getMessage(), e); } catch (SQLException e) { throw new JobPersistenceException("Couldn't retrieve job: " + e.getMessage(), e); } } - protected boolean removeTrigger(Connection conn, SchedulingContext ctxt, - String triggerName, String groupName) - throws JobPersistenceException { - boolean removedTrigger = false; + /** + *

+ * Remove (delete) the {@link 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. + *

+ * + * @return true if a Trigger with the given + * name & group was found and removed from the store. + */ + public boolean removeTrigger(final TriggerKey triggerKey) throws JobPersistenceException { + return (Boolean) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return removeTrigger(conn, triggerKey) ? + Boolean.TRUE : Boolean.FALSE; + } + }); + } + + protected boolean removeTrigger(Connection conn, TriggerKey key) + throws JobPersistenceException { + boolean removedTrigger; try { // this must be called before we delete the trigger, obviously JobDetail job = getDelegate().selectJobForTrigger(conn, - triggerName, groupName, getClassLoadHelper()); + getClassLoadHelper(), key, false); - getDelegate().deleteSimpleTrigger(conn, triggerName, groupName); - getDelegate().deleteCronTrigger(conn, triggerName, groupName); - getDelegate().deleteBlobTrigger(conn, triggerName, groupName); - getDelegate().deleteTriggerListeners(conn, triggerName, groupName); - removedTrigger = (getDelegate().deleteTrigger(conn, triggerName, - groupName) > 0); + removedTrigger = + deleteTriggerAndChildren(conn, key); if (null != job && !job.isDurable()) { int numTriggers = getDelegate().selectNumTriggersForJob(conn, - job.getName(), job.getGroup()); + job.getKey()); if (numTriggers == 0) { - removeJob(conn, ctxt, job.getName(), job.getGroup(), true); + // Don't call removeJob() because we don't want to check for + // triggers again. + deleteJobAndChildren(conn, job.getKey()); } } } catch (ClassNotFoundException e) { @@ -1077,118 +1461,204 @@ return removedTrigger; } - protected boolean replaceTrigger(Connection conn, SchedulingContext ctxt, - String triggerName, String groupName, Trigger newTrigger) - throws JobPersistenceException { - boolean removedTrigger = false; + /** + * @see org.quartz.spi.JobStore#replaceTrigger(TriggerKey, OperableTrigger) + */ + public boolean replaceTrigger(final TriggerKey triggerKey, + final OperableTrigger newTrigger) throws JobPersistenceException { + return (Boolean) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return replaceTrigger(conn, triggerKey, newTrigger) ? + Boolean.TRUE : Boolean.FALSE; + } + }); + } + + protected boolean replaceTrigger(Connection conn, + TriggerKey key, OperableTrigger newTrigger) + throws JobPersistenceException { try { // this must be called before we delete the trigger, obviously JobDetail job = getDelegate().selectJobForTrigger(conn, - triggerName, groupName, getClassLoadHelper()); + getClassLoadHelper(), key); - if(job == null) + if (job == null) { return false; + } - if(!newTrigger.getJobName().equals(job.getName()) || - !newTrigger.getJobGroup().equals(job.getGroup())) + if (!newTrigger.getJobKey().equals(job.getKey())) { throw new JobPersistenceException("New trigger is not related to the same job as the old trigger."); + } - getDelegate().deleteSimpleTrigger(conn, triggerName, groupName); - getDelegate().deleteCronTrigger(conn, triggerName, groupName); - getDelegate().deleteBlobTrigger(conn, triggerName, groupName); - getDelegate().deleteTriggerListeners(conn, triggerName, groupName); - removedTrigger = (getDelegate().deleteTrigger(conn, triggerName, - groupName) > 0); + boolean removedTrigger = + deleteTriggerAndChildren(conn, key); - storeTrigger(conn, ctxt, newTrigger, job, false, STATE_WAITING, false, false); + storeTrigger(conn, 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); } - - return removedTrigger; } - protected Trigger retrieveTrigger(Connection conn, SchedulingContext ctxt, - String triggerName, String groupName) - throws JobPersistenceException { + /** + *

+ * Retrieve the given {@link org.quartz.Trigger}. + *

+ * + * @return The desired Trigger, or null if there is no + * match. + */ + public OperableTrigger retrieveTrigger(final TriggerKey triggerKey) throws JobPersistenceException { + return (OperableTrigger)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return retrieveTrigger(conn, triggerKey); + } + }); + } + + protected OperableTrigger retrieveTrigger(Connection conn, TriggerKey key) + throws JobPersistenceException { try { - Trigger trigger = getDelegate().selectTrigger(conn, triggerName, - groupName); - if (trigger == null) return null; - String[] listeners = getDelegate().selectTriggerListeners(conn, - triggerName, groupName); - for (int i = 0; i < listeners.length; ++i) { - trigger.addTriggerListener(listeners[i]); - } - return trigger; + return getDelegate().selectTrigger(conn, key); } catch (Exception e) { throw new JobPersistenceException("Couldn't retrieve trigger: " + e.getMessage(), e); } } - public int getTriggerState(Connection conn, SchedulingContext ctxt, - String triggerName, String groupName) - throws JobPersistenceException { + /** + *

+ * Get the current state of the identified {@link Trigger}. + *

+ * + * @see TriggerState#NORMAL + * @see TriggerState#PAUSED + * @see TriggerState#COMPLETE + * @see TriggerState#ERROR + * @see TriggerState#NONE + */ + public TriggerState getTriggerState(final TriggerKey triggerKey) throws JobPersistenceException { + return (TriggerState)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getTriggerState(conn, triggerKey); + } + }); + } + + public TriggerState getTriggerState(Connection conn, TriggerKey key) + throws JobPersistenceException { try { - String ts = getDelegate().selectTriggerState(conn, triggerName, - groupName); + String ts = getDelegate().selectTriggerState(conn, key); - if (ts == null) return Trigger.STATE_NONE; + if (ts == null) { + return TriggerState.NONE; + } - if (ts.equals(STATE_DELETED)) return Trigger.STATE_NONE; + if (ts.equals(STATE_DELETED)) { + return TriggerState.NONE; + } - if (ts.equals(STATE_COMPLETE)) return Trigger.STATE_COMPLETE; + if (ts.equals(STATE_COMPLETE)) { + return TriggerState.COMPLETE; + } - if (ts.equals(STATE_PAUSED)) return Trigger.STATE_PAUSED; + if (ts.equals(STATE_PAUSED)) { + return TriggerState.PAUSED; + } - if (ts.equals(STATE_PAUSED_BLOCKED)) return Trigger.STATE_PAUSED; + if (ts.equals(STATE_PAUSED_BLOCKED)) { + return TriggerState.PAUSED; + } - if (ts.equals(STATE_ERROR)) return Trigger.STATE_ERROR; + if (ts.equals(STATE_ERROR)) { + return TriggerState.ERROR; + } - if (ts.equals(STATE_BLOCKED)) return Trigger.STATE_BLOCKED; + if (ts.equals(STATE_BLOCKED)) { + return TriggerState.BLOCKED; + } - return Trigger.STATE_NORMAL; + return TriggerState.NORMAL; } catch (SQLException e) { throw new JobPersistenceException( - "Couldn't determine state of trigger (" + groupName + "." - + triggerName + "): " + e.getMessage(), e); + "Couldn't determine state of trigger (" + key + "): " + e.getMessage(), e); } } - protected void storeCalendar(Connection conn, SchedulingContext ctxt, + /** + *

+ * Store the given {@link org.quartz.Calendar}. + *

+ * + * @param calName + * The name of the calendar. + * @param calendar + * The 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 String calName, + final Calendar calendar, final boolean replaceExisting, final boolean updateTriggers) + throws JobPersistenceException { + executeInLock( + (isLockOnInsert() || updateTriggers) ? LOCK_TRIGGER_ACCESS : null, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + storeCalendar(conn, calName, calendar, replaceExisting, updateTriggers); + } + }); + } + + protected void storeCalendar(Connection conn, String calName, Calendar calendar, boolean replaceExisting, boolean updateTriggers) - throws ObjectAlreadyExistsException, JobPersistenceException { + throws JobPersistenceException { try { boolean existingCal = calendarExists(conn, calName); - if (existingCal && !replaceExisting) { throw new ObjectAlreadyExistsException( - "Calendar with name '" + calName + "' already exists."); } + 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 (getDelegate().updateCalendar(conn, calName, calendar) < 1) { + throw new JobPersistenceException( + "Couldn't store calendar. Update failed."); + } if(updateTriggers) { - Trigger[] trigs = getDelegate().selectTriggersForCalendar(conn, calName); + List 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); + for(OperableTrigger trigger: trigs) { + trigger.updateWithNewCalendar(calendar, getMisfireThreshold()); + storeTrigger(conn, trigger, null, true, STATE_WAITING, false, false); } } } else { - if (getDelegate().insertCalendar(conn, calName, calendar) < 1) { throw new JobPersistenceException( - "Couldn't store calendar. Insert failed."); } + if (getDelegate().insertCalendar(conn, calName, calendar) < 1) { + throw new JobPersistenceException( + "Couldn't store calendar. Insert failed."); + } } - calendarCache.put(calName, calendar); // lazy-cache + if (!isClustered) { + calendarCache.put(calName, calendar); // lazy-cache + } } catch (IOException e) { throw new JobPersistenceException( @@ -1204,7 +1674,7 @@ } protected boolean calendarExists(Connection conn, String calName) - throws JobPersistenceException { + throws JobPersistenceException { try { return getDelegate().calendarExists(conn, calName); } catch (SQLException e) { @@ -1214,13 +1684,44 @@ } } - protected boolean removeCalendar(Connection conn, SchedulingContext ctxt, + /** + *

+ * Remove (delete) the {@link org.quartz.Calendar} with the + * given name. + *

+ * + *

+ * If removal of the Calendar would result in + * Triggers pointing to non-existent calendars, then a + * JobPersistenceException will be thrown.

+ * * + * @param calName The name of the Calendar to be removed. + * @return true if a Calendar with the given name + * was found and removed from the store. + */ + public boolean removeCalendar(final String calName) + throws JobPersistenceException { + return (Boolean) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return removeCalendar(conn, calName) ? + Boolean.TRUE : Boolean.FALSE; + } + }); + } + + protected boolean removeCalendar(Connection conn, String calName) throws JobPersistenceException { try { - if (getDelegate().calendarIsReferenced(conn, calName)) { throw new JobPersistenceException( - "Calender cannot be removed if it referenced by a trigger!"); } + if (getDelegate().calendarIsReferenced(conn, calName)) { + throw new JobPersistenceException( + "Calender cannot be removed if it referenced by a trigger!"); + } - calendarCache.remove(calName); + if (!isClustered) { + calendarCache.remove(calName); + } return (getDelegate().deleteCalendar(conn, calName) > 0); } catch (SQLException e) { @@ -1229,17 +1730,41 @@ } } + /** + *

+ * Retrieve the given {@link org.quartz.Trigger}. + *

+ * + * @param calName + * The name of the Calendar to be retrieved. + * @return The desired Calendar, or null if there is no + * match. + */ + public Calendar retrieveCalendar(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, calName); + } + }); + } + protected Calendar retrieveCalendar(Connection conn, - SchedulingContext ctxt, String calName) - throws JobPersistenceException { - // all calendars are persistent, but we lazy-cache them during run - // time... - Calendar cal = (Calendar) calendarCache.get(calName); - if (cal != null) return cal; + 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 : calendarCache.get(calName); + if (cal != null) { + return cal; + } try { cal = getDelegate().selectCalendar(conn, calName); - calendarCache.put(calName, cal); // lazy-cache... + if (!isClustered) { + calendarCache.put(calName, cal); // lazy-cache... + } return cal; } catch (ClassNotFoundException e) { throw new JobPersistenceException( @@ -1255,8 +1780,24 @@ } } - protected int getNumberOfJobs(Connection conn, SchedulingContext ctxt) - throws JobPersistenceException { + /** + *

+ * Get the number of {@link org.quartz.Job} s that are + * stored in the JobStore. + *

+ */ + public int getNumberOfJobs() + throws JobPersistenceException { + return (Integer) executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getNumberOfJobs(conn); + } + }); + } + + protected int getNumberOfJobs(Connection conn) + throws JobPersistenceException { try { return getDelegate().selectNumJobs(conn); } catch (SQLException e) { @@ -1265,8 +1806,24 @@ } } - protected int getNumberOfTriggers(Connection conn, SchedulingContext ctxt) - throws JobPersistenceException { + /** + *

+ * Get the number of {@link org.quartz.Trigger} s that are + * stored in the JobsStore. + *

+ */ + public int getNumberOfTriggers() + throws JobPersistenceException { + return (Integer) executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getNumberOfTriggers(conn); + } + }); + } + + protected int getNumberOfTriggers(Connection conn) + throws JobPersistenceException { try { return getDelegate().selectNumTriggers(conn); } catch (SQLException e) { @@ -1275,8 +1832,24 @@ } } - protected int getNumberOfCalendars(Connection conn, SchedulingContext ctxt) - throws JobPersistenceException { + /** + *

+ * Get the number of {@link org.quartz.Calendar} s that are + * stored in the JobsStore. + *

+ */ + public int getNumberOfCalendars() + throws JobPersistenceException { + return (Integer) executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getNumberOfCalendars(conn); + } + }); + } + + protected int getNumberOfCalendars(Connection conn) + throws JobPersistenceException { try { return getDelegate().selectNumCalendars(conn); } catch (SQLException e) { @@ -1285,27 +1858,147 @@ } } - protected String[] getJobNames(Connection conn, SchedulingContext ctxt, - String groupName) throws JobPersistenceException { - String[] jobNames = null; + /** + *

+ * Get the names of all of the {@link org.quartz.Job} s that + * matcher the given groupMatcher. + *

+ * + *

+ * If there are no jobs in the given group name, the result should be an empty Set + *

+ */ + @SuppressWarnings("unchecked") + public Set getJobKeys(final GroupMatcher matcher) + throws JobPersistenceException { + return (Set)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getJobNames(conn, matcher); + } + }); + } + + protected Set getJobNames(Connection conn, + GroupMatcher matcher) throws JobPersistenceException { + Set jobNames; try { - jobNames = getDelegate().selectJobsInGroup(conn, groupName); + jobNames = getDelegate().selectJobsInGroup(conn, matcher); } catch (SQLException e) { throw new JobPersistenceException("Couldn't obtain job names: " + e.getMessage(), e); } return jobNames; } + + + /** + * Determine whether a {@link Job} with the given identifier already + * exists within the scheduler. + * + * @param jobKey the identifier to check for + * @return true if a Job exists with the given identifier + * @throws JobPersistenceException + */ + public boolean checkExists(final JobKey jobKey) throws JobPersistenceException { + return (Boolean)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return checkExists(conn, jobKey); + } + }); + } + + protected boolean checkExists(Connection conn, JobKey jobKey) throws JobPersistenceException { + try { + return getDelegate().jobExists(conn, jobKey); + } catch (SQLException e) { + throw new JobPersistenceException("Couldn't check for existence of job: " + + e.getMessage(), e); + } + } + + /** + * Determine whether a {@link Trigger} with the given identifier already + * exists within the scheduler. + * + * @param triggerKey the identifier to check for + * @return true if a Trigger exists with the given identifier + * @throws JobPersistenceException + */ + public boolean checkExists(final TriggerKey triggerKey) throws JobPersistenceException { + return (Boolean)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return checkExists(conn, triggerKey); + } + }); + } + + protected boolean checkExists(Connection conn, TriggerKey triggerKey) throws JobPersistenceException { + try { + return getDelegate().triggerExists(conn, triggerKey); + } catch (SQLException e) { + throw new JobPersistenceException("Couldn't check for existence of job: " + + e.getMessage(), e); + } + } - protected String[] getTriggerNames(Connection conn, SchedulingContext ctxt, - String groupName) throws JobPersistenceException { + /** + * Clear (delete!) all scheduling data - all {@link Job}s, {@link Trigger}s + * {@link Calendar}s. + * + * @throws JobPersistenceException + */ + public void clearAllSchedulingData() throws JobPersistenceException { + executeInLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + clearAllSchedulingData(conn); + } + }); + } + + protected void clearAllSchedulingData(Connection conn) throws JobPersistenceException { + try { + getDelegate().clearData(conn); + } catch (SQLException e) { + throw new JobPersistenceException("Error clearing scheduling data: " + e.getMessage(), e); + } + } + + /** + *

+ * Get the names of all of the {@link org.quartz.Trigger} s + * that match the given group Matcher. + *

+ * + *

+ * If there are no triggers in the given group name, the result should be a + * an empty Set (not null). + *

+ */ + @SuppressWarnings("unchecked") + public Set getTriggerKeys(final GroupMatcher matcher) + throws JobPersistenceException { + return (Set)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getTriggerNames(conn, matcher); + } + }); + } + + protected Set getTriggerNames(Connection conn, + GroupMatcher matcher) throws JobPersistenceException { - String[] trigNames = null; + Set trigNames; try { - trigNames = getDelegate().selectTriggersInGroup(conn, groupName); + trigNames = getDelegate().selectTriggersInGroup(conn, matcher); } catch (SQLException e) { throw new JobPersistenceException("Couldn't obtain trigger names: " + e.getMessage(), e); @@ -1314,11 +2007,34 @@ return trigNames; } - protected String[] getJobGroupNames(Connection conn, SchedulingContext ctxt) - throws JobPersistenceException { - String[] groupNames = null; + /** + *

+ * Get the names of all of the {@link org.quartz.Job} + * groups. + *

+ * + *

+ * If there are no known group names, the result should be a zero-length + * array (not null). + *

+ */ + @SuppressWarnings("unchecked") + public List getJobGroupNames() + throws JobPersistenceException { + return (List)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getJobGroupNames(conn); + } + }); + } + + protected List getJobGroupNames(Connection conn) + throws JobPersistenceException { + List groupNames; + try { groupNames = getDelegate().selectJobGroups(conn); } catch (SQLException e) { @@ -1329,10 +2045,31 @@ return groupNames; } - protected String[] getTriggerGroupNames(Connection conn, - SchedulingContext ctxt) throws JobPersistenceException { + /** + *

+ * Get the names of all of the {@link org.quartz.Trigger} + * groups. + *

+ * + *

+ * If there are no known group names, the result should be a zero-length + * array (not null). + *

+ */ + @SuppressWarnings("unchecked") + public List getTriggerGroupNames() + throws JobPersistenceException { + return (List)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getTriggerGroupNames(conn); + } + }); + } + + protected List getTriggerGroupNames(Connection conn) throws JobPersistenceException { - String[] groupNames = null; + List groupNames; try { groupNames = getDelegate().selectTriggerGroups(conn); @@ -1344,8 +2081,30 @@ return groupNames; } - protected String[] getCalendarNames(Connection conn, SchedulingContext ctxt) - throws JobPersistenceException { + /** + *

+ * Get the names of all of the {@link 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). + *

+ */ + @SuppressWarnings("unchecked") + public List getCalendarNames() + throws JobPersistenceException { + return (List)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getCalendarNames(conn); + } + }); + } + + protected List getCalendarNames(Connection conn) + throws JobPersistenceException { try { return getDelegate().selectCalendars(conn); } catch (SQLException e) { @@ -1354,141 +2113,202 @@ } } - protected Trigger[] getTriggersForJob(Connection conn, - SchedulingContext ctxt, String jobName, String groupName) - throws JobPersistenceException { - Trigger[] array = 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. + *

+ */ + @SuppressWarnings("unchecked") + public List getTriggersForJob(final JobKey jobKey) throws JobPersistenceException { + return (List)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getTriggersForJob(conn, jobKey); + } + }); + } + + protected List getTriggersForJob(Connection conn, + JobKey key) + throws JobPersistenceException { + List list; try { - array = getDelegate() - .selectTriggersForJob(conn, jobName, groupName); + list = getDelegate() + .selectTriggersForJob(conn, key); } catch (Exception e) { throw new JobPersistenceException( "Couldn't obtain triggers for job: " + e.getMessage(), e); } - return array; + return list; } /** *

* Pause the {@link org.quartz.Trigger} with the given name. *

* - * @see #resumeTrigger(Connection, SchedulingContext, String, String) + * @see #resumeTrigger(TriggerKey) */ - public void pauseTrigger(Connection conn, SchedulingContext ctxt, - String triggerName, String groupName) - throws JobPersistenceException { + public void pauseTrigger(final TriggerKey triggerKey) throws JobPersistenceException { + executeInLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + pauseTrigger(conn, triggerKey); + } + }); + } + + /** + *

+ * Pause the {@link org.quartz.Trigger} with the given name. + *

+ * + * @see #resumeTrigger(Connection, TriggerKey) + */ + public void pauseTrigger(Connection conn, + TriggerKey triggerKey) + throws JobPersistenceException { try { String oldState = getDelegate().selectTriggerState(conn, - triggerName, groupName); + triggerKey); if (oldState.equals(STATE_WAITING) || oldState.equals(STATE_ACQUIRED)) { - getDelegate().updateTriggerState(conn, triggerName, - groupName, STATE_PAUSED); + getDelegate().updateTriggerState(conn, triggerKey, + STATE_PAUSED); + } else if (oldState.equals(STATE_BLOCKED)) { + getDelegate().updateTriggerState(conn, triggerKey, + STATE_PAUSED_BLOCKED); } - else if (oldState.equals(STATE_BLOCKED)) { - getDelegate().updateTriggerState(conn, triggerName, - groupName, STATE_PAUSED_BLOCKED); - } } catch (SQLException e) { throw new JobPersistenceException("Couldn't pause trigger '" - + groupName + "." + triggerName + "': " + e.getMessage(), e); + + triggerKey + "': " + e.getMessage(), e); } } - protected String getStatusForResumedTrigger(Connection conn, - SchedulingContext ctxt, TriggerStatus status) - throws JobPersistenceException { + /** + *

+ * Pause the {@link org.quartz.Job} with the given name - by + * pausing all of its current Triggers. + *

+ * + * @see #resumeJob(JobKey) + */ + public void pauseJob(final JobKey jobKey) throws JobPersistenceException { + executeInLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + List triggers = getTriggersForJob(conn, jobKey); + for (OperableTrigger trigger: triggers) { + pauseTrigger(conn, trigger.getKey()); + } + } + }); + } + + /** + *

+ * Pause all of the {@link org.quartz.Job}s matching the given + * groupMatcher - by pausing all of their Triggers. + *

+ * + * @see #resumeJobs(org.quartz.impl.matchers.GroupMatcher) + */ + @SuppressWarnings("unchecked") + public Set pauseJobs(final GroupMatcher matcher) + throws JobPersistenceException { + return (Set) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Set execute(final Connection conn) throws JobPersistenceException { + Set groupNames = new HashSet(); + Set jobNames = getJobNames(conn, matcher); - try { - String newState = STATE_WAITING; + for (JobKey jobKey : jobNames) { + List triggers = getTriggersForJob(conn, jobKey); + for (OperableTrigger trigger : triggers) { + pauseTrigger(conn, trigger.getKey()); + } + groupNames.add(jobKey.getGroup()); + } - List lst = getDelegate() - .selectFiredTriggerRecordsByJob(conn, - status.getJobKey().getName(), - status.getJobKey().getGroup()); - - if (lst.size() > 0) { - FiredTriggerRecord rec = (FiredTriggerRecord) lst.get(0); - if (rec.isJobIsStateful()) // TODO: worry about - // failed/recovering/volatile job - // states? - newState = STATE_BLOCKED; + return groupNames; + } } - - return newState; - - } catch (SQLException e) { - throw new JobPersistenceException( - "Couldn't determine new state in order to resume trigger '" - + status.getKey().getGroup() + "." - + status.getKey().getName() + "': " - + e.getMessage(), e); - } - + ); } + + /** + * Determines if a Trigger for the given job should be blocked. + * State can only transition to STATE_PAUSED_BLOCKED/BLOCKED from + * PAUSED/STATE_WAITING respectively. + * + * @return STATE_PAUSED_BLOCKED, BLOCKED, or the currentState. + */ + protected String checkBlockedState( + Connection conn, JobKey jobKey, String currentState) + throws JobPersistenceException { - protected String getNewStatusForTrigger(Connection conn, - SchedulingContext ctxt, String jobName, String groupName) - throws JobPersistenceException { - + // State can only transition to BLOCKED from PAUSED or WAITING. + if ((!currentState.equals(STATE_WAITING)) && + (!currentState.equals(STATE_PAUSED))) { + return currentState; + } + try { - String newState = STATE_WAITING; + List lst = getDelegate().selectFiredTriggerRecordsByJob(conn, + jobKey.getName(), jobKey.getGroup()); - List lst = getDelegate().selectFiredTriggerRecordsByJob(conn, - jobName, groupName); - if (lst.size() > 0) { - FiredTriggerRecord rec = (FiredTriggerRecord) lst.get(0); - if (rec.isJobIsStateful()) // TODO: worry about - // failed/recovering/volatile job - // states? - newState = STATE_BLOCKED; + FiredTriggerRecord rec = lst.get(0); + if (rec.isJobDisallowsConcurrentExecution()) { // OLD_TODO: worry about failed/recovering/volatile job states? + return (STATE_PAUSED.equals(currentState)) ? STATE_PAUSED_BLOCKED : STATE_BLOCKED; + } } - return newState; - + return currentState; } catch (SQLException e) { throw new JobPersistenceException( - "Couldn't determine state for new trigger: " - + e.getMessage(), e); + "Couldn't determine if trigger should be in a blocked state '" + + jobKey + "': " + + e.getMessage(), e); } } - /* - * private List findTriggersToBeBlocked(Connection conn, SchedulingContext - * ctxt, String groupName) throws JobPersistenceException { + /** + *

+ * Resume (un-pause) the {@link org.quartz.Trigger} with the + * given name. + *

* - * try { List blockList = new LinkedList(); + *

+ * If the Trigger missed one or more fire-times, then the + * Trigger's misfire instruction will be applied. + *

* - * List affectingJobs = - * getDelegate().selectStatefulJobsOfTriggerGroup(conn, groupName); - * - * Iterator itr = affectingJobs.iterator(); while(itr.hasNext()) { Key - * jobKey = (Key) itr.next(); - * - * List lst = getDelegate().selectFiredTriggerRecordsByJob(conn, - * jobKey.getName(), jobKey.getGroup()); - * - * This logic is BROKEN... - * - * if(lst.size() > 0) { FiredTriggerRecord rec = - * (FiredTriggerRecord)lst.get(0); if(rec.isJobIsStateful()) // TODO: worry - * about failed/recovering/volatile job states? blockList.add( - * rec.getTriggerKey() ); } } - * - * - * return blockList; } catch (SQLException e) { throw new - * JobPersistenceException ("Couldn't determine states of resumed triggers - * in group '" + groupName + "': " + e.getMessage(), e); } } + * @see #pauseTrigger(TriggerKey) */ - + public void resumeTrigger(final TriggerKey triggerKey) throws JobPersistenceException { + executeInLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + resumeTrigger(conn, triggerKey); + } + }); + } + /** *

* Resume (un-pause) the {@link org.quartz.Trigger} with the @@ -1500,85 +2320,195 @@ * Trigger's misfire instruction will be applied. *

* - * @see #pauseTrigger(Connection, SchedulingContext, String, String) + * @see #pauseTrigger(Connection, TriggerKey) */ - public void resumeTrigger(Connection conn, SchedulingContext ctxt, - String triggerName, String groupName) - throws JobPersistenceException { + public void resumeTrigger(Connection conn, + TriggerKey key) + throws JobPersistenceException { try { TriggerStatus status = getDelegate().selectTriggerStatus(conn, - triggerName, groupName); + key); - if (status == null || status.getNextFireTime() == null) return; + if (status == null || status.getNextFireTime() == null) { + return; + } boolean blocked = false; - if(STATE_PAUSED_BLOCKED.equals(status.getStatus())) + if(STATE_PAUSED_BLOCKED.equals(status.getStatus())) { blocked = true; + } - String newState = getStatusForResumedTrigger(conn, ctxt, status); + String newState = checkBlockedState(conn, status.getJobKey(), STATE_WAITING); boolean misfired = false; - if (status.getNextFireTime().before(new Date())) { - misfired = updateMisfiredTrigger(conn, ctxt, triggerName, groupName, - newState, true); + if (schedulerRunning && status.getNextFireTime().before(new Date())) { + misfired = updateMisfiredTrigger(conn, key, + newState, true); } if(!misfired) { - if(blocked) + if(blocked) { getDelegate().updateTriggerStateFromOtherState(conn, - triggerName, groupName, newState, STATE_PAUSED_BLOCKED); - else + key, newState, STATE_PAUSED_BLOCKED); + } else { getDelegate().updateTriggerStateFromOtherState(conn, - triggerName, groupName, newState, STATE_PAUSED); + key, newState, STATE_PAUSED); + } } } catch (SQLException e) { throw new JobPersistenceException("Couldn't resume trigger '" - + groupName + "." + triggerName + "': " + e.getMessage(), e); + + key + "': " + e.getMessage(), e); } } /** *

- * Pause all of the {@link org.quartz.Trigger}s in the - * given group. + * Resume (un-pause) the {@link org.quartz.Job} with the + * given name. *

* - * @see #resumeTriggerGroup(Connection, SchedulingContext, String) + *

+ * If any of the Job'sTrigger s missed one + * or more fire-times, then the Trigger's misfire + * instruction will be applied. + *

+ * + * @see #pauseJob(JobKey) */ - public void pauseTriggerGroup(Connection conn, SchedulingContext ctxt, - String groupName) throws JobPersistenceException { + public void resumeJob(final JobKey jobKey) throws JobPersistenceException { + executeInLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + List triggers = getTriggersForJob(conn, jobKey); + for (OperableTrigger trigger: triggers) { + resumeTrigger(conn, trigger.getKey()); + } + } + }); + } + + /** + *

+ * Resume (un-pause) all of the {@link 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. + *

+ * + * @see #pauseJobs(org.quartz.impl.matchers.GroupMatcher) + */ + @SuppressWarnings("unchecked") + public Set resumeJobs(final GroupMatcher matcher) + throws JobPersistenceException { + return (Set) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Set execute(Connection conn) throws JobPersistenceException { + Set jobKeys = getJobNames(conn, matcher); + Set groupNames = new HashSet(); + for (JobKey jobKey: jobKeys) { + List triggers = getTriggersForJob(conn, jobKey); + for (OperableTrigger trigger: triggers) { + resumeTrigger(conn, trigger.getKey()); + } + groupNames.add(jobKey.getGroup()); + } + return groupNames; + } + }); + } + + /** + *

+ * Pause all of the {@link org.quartz.Trigger}s matching the + * given groupMatcher. + *

+ * + * @see #resumeTriggerGroup(java.sql.Connection, org.quartz.impl.matchers.GroupMatcher) + */ + @SuppressWarnings("unchecked") + public Set pauseTriggers(final GroupMatcher matcher) + throws JobPersistenceException { + return (Set) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Set execute(Connection conn) throws JobPersistenceException { + return pauseTriggerGroup(conn, matcher); + } + }); + } + + /** + *

+ * Pause all of the {@link org.quartz.Trigger}s matching the + * given groupMatcher. + *

+ * + * @see #resumeTriggerGroup(java.sql.Connection, org.quartz.impl.matchers.GroupMatcher) + */ + public Set pauseTriggerGroup(Connection conn, + GroupMatcher matcher) throws JobPersistenceException { + try { getDelegate().updateTriggerGroupStateFromOtherStates( - conn, groupName, STATE_PAUSED, STATE_ACQUIRED, + conn, matcher, STATE_PAUSED, STATE_ACQUIRED, STATE_WAITING, STATE_WAITING); getDelegate().updateTriggerGroupStateFromOtherState( - conn, groupName, STATE_PAUSED_BLOCKED, STATE_BLOCKED); + conn, matcher, STATE_PAUSED_BLOCKED, STATE_BLOCKED); + + List groups = getDelegate().selectTriggerGroups(conn, matcher); - if (!getDelegate().isTriggerGroupPaused(conn, groupName)) { - getDelegate().insertPausedTriggerGroup(conn, groupName); + // make sure to account for an exact group match for a group that doesn't yet exist + StringMatcher.StringOperatorName operator = matcher.getCompareWithOperator(); + if (operator.equals(StringOperatorName.EQUALS) && !groups.contains(matcher.getCompareToValue())) { + groups.add(matcher.getCompareToValue()); } + for (String group : groups) { + if (!getDelegate().isTriggerGroupPaused(conn, group)) { + getDelegate().insertPausedTriggerGroup(conn, group); + } + } + + return new HashSet(groups); + } catch (SQLException e) { throw new JobPersistenceException("Couldn't pause trigger group '" - + groupName + "': " + e.getMessage(), e); + + matcher + "': " + e.getMessage(), e); } } + @SuppressWarnings("unchecked") + public Set getPausedTriggerGroups() + throws JobPersistenceException { + return (Set)executeWithoutLock( // no locks necessary for read... + new TransactionCallback() { + public Object execute(Connection conn) throws JobPersistenceException { + return getPausedTriggerGroups(conn); + } + }); + } + /** *

* Pause all of the {@link org.quartz.Trigger}s in the * given group. *

* - * @see #resumeTriggerGroup(Connection, SchedulingContext, String) + * @see #resumeTriggers(org.quartz.impl.matchers.GroupMatcher) */ - public Set getPausedTriggerGroups(Connection conn, SchedulingContext ctxt) + public Set getPausedTriggerGroups(Connection conn) throws JobPersistenceException { try { @@ -1592,37 +2522,67 @@ /** *

* Resume (un-pause) all of the {@link org.quartz.Trigger}s - * in the given group. + * matching the given groupMatcher. *

* *

* If any Trigger missed one or more fire-times, then the * Trigger's misfire instruction will be applied. *

* - * @see #pauseTriggerGroup(Connection, SchedulingContext, String) + * @see #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) */ - public void resumeTriggerGroup(Connection conn, SchedulingContext ctxt, - String groupName) throws JobPersistenceException { + @SuppressWarnings("unchecked") + public Set resumeTriggers(final GroupMatcher matcher) + throws JobPersistenceException { + return (Set) executeInLock( + LOCK_TRIGGER_ACCESS, + new TransactionCallback() { + public Set execute(Connection conn) throws JobPersistenceException { + return resumeTriggerGroup(conn, matcher); + } + }); + } + + /** + *

+ * Resume (un-pause) all of the {@link org.quartz.Trigger}s + * matching the given groupMatcher. + *

+ * + *

+ * If any Trigger missed one or more fire-times, then the + * Trigger's misfire instruction will be applied. + *

+ * + * @see #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) + */ + public Set resumeTriggerGroup(Connection conn, + GroupMatcher matcher) throws JobPersistenceException { + try { - getDelegate().deletePausedTriggerGroup(conn, groupName); + getDelegate().deletePausedTriggerGroup(conn, matcher); + HashSet groups = new HashSet(); - String[] trigNames = getDelegate().selectTriggersInGroup(conn, - groupName); + Set keys = getDelegate().selectTriggersInGroup(conn, + matcher); - for (int i = 0; i < trigNames.length; i++) { - resumeTrigger(conn, ctxt, trigNames[i], groupName); + for (TriggerKey key: keys) { + resumeTrigger(conn, key); + groups.add(key.getGroup()); } - // TODO: find an efficient way to resume triggers (better than the + return groups; + + // FUTURE_TODO: find an efficient way to resume triggers (better than the // above)... logic below is broken because of // findTriggersToBeBlocked() /* * int res = * getDelegate().updateTriggerGroupStateFromOtherState(conn, - * groupName, STATE_WAITING, STATE_PAUSED); + * groupName, STATE_WAITING, PAUSED); * * if(res > 0) { * @@ -1634,24 +2594,24 @@ * getDelegate().selectMisfiredTriggersInGroupInState(conn, * groupName, STATE_WAITING, misfireTime); * - * List blockedTriggers = findTriggersToBeBlocked(conn, ctxt, + * List blockedTriggers = findTriggersToBeBlocked(conn, * groupName); * * Iterator itr = blockedTriggers.iterator(); while(itr.hasNext()) { * Key key = (Key)itr.next(); * getDelegate().updateTriggerState(conn, key.getName(), - * key.getGroup(), STATE_BLOCKED); } + * key.getGroup(), BLOCKED); } * * for(int i=0; i < misfires.length; i++) { String * newState = STATE_WAITING; * if(blockedTriggers.contains(misfires[i])) newState = - * STATE_BLOCKED; updateMisfiredTrigger(conn, ctxt, + * BLOCKED; updateMisfiredTrigger(conn, * misfires[i].getName(), misfires[i].getGroup(), newState, true); } } */ } catch (SQLException e) { throw new JobPersistenceException("Couldn't pause trigger group '" - + groupName + "': " + e.getMessage(), e); + + matcher + "': " + e.getMessage(), e); } } @@ -1666,16 +2626,40 @@ * instructions WILL be applied. *

* - * @see #resumeAll(SchedulingContext) - * @see #pauseTriggerGroup(SchedulingContext, String) + * @see #resumeAll() + * @see #pauseTriggerGroup(java.sql.Connection, org.quartz.impl.matchers.GroupMatcher) */ - public void pauseAll(Connection conn, SchedulingContext ctxt) - throws JobPersistenceException { + public void pauseAll() throws JobPersistenceException { + executeInLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + pauseAll(conn); + } + }); + } + + /** + *

+ * Pause all triggers - equivalent of calling pauseTriggerGroup(group) + * on every group. + *

+ * + *

+ * When resumeAll() is called (to un-pause), trigger misfire + * instructions WILL be applied. + *

+ * + * @see #resumeAll(Connection) + * @see #pauseTriggerGroup(java.sql.Connection, org.quartz.impl.matchers.GroupMatcher) + */ + public void pauseAll(Connection conn) + throws JobPersistenceException { - String[] names = getTriggerGroupNames(conn, ctxt); + List names = getTriggerGroupNames(conn); - for (int i = 0; i < names.length; i++) { - pauseTriggerGroup(conn, ctxt, names[i]); + for (String name: names) { + pauseTriggerGroup(conn, GroupMatcher.triggerGroupEquals(name)); } try { @@ -1691,6 +2675,30 @@ } /** + *

+ * 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. + *

+ * + * @see #pauseAll() + */ + public void resumeAll() + throws JobPersistenceException { + executeInLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + resumeAll(conn); + } + }); + } + + /** * protected *

* Resume (un-pause) all triggers - equivalent of calling resumeTriggerGroup(group) @@ -1702,15 +2710,15 @@ * Trigger's misfire instruction will be applied. *

* - * @see #pauseAll(SchedulingContext) + * @see #pauseAll(Connection) */ - public void resumeAll(Connection conn, SchedulingContext ctxt) - throws JobPersistenceException { + public void resumeAll(Connection conn) + throws JobPersistenceException { - String[] names = getTriggerGroupNames(conn, ctxt); + List names = getTriggerGroupNames(conn); - for (int i = 0; i < names.length; i++) { - resumeTriggerGroup(conn, ctxt, names[i]); + for (String name: names) { + resumeTriggerGroup(conn, GroupMatcher.triggerGroupEquals(name)); } try { @@ -1719,7 +2727,6 @@ throw new JobPersistenceException( "Couldn't resume all trigger groups: " + e.getMessage(), e); } - } private static long ftrCtr = System.currentTimeMillis(); @@ -1728,116 +2735,252 @@ return getInstanceId() + ftrCtr++; } - // TODO: this really ought to return something like a FiredTriggerBundle, + /** + *

+ * Get a handle to the next N triggers to be fired, and mark them as 'reserved' + * by the calling scheduler. + *

+ * + * @see #releaseAcquiredTrigger(OperableTrigger) + */ + @SuppressWarnings("unchecked") + public List acquireNextTriggers(final long noLaterThan, final int maxCount, final long timeWindow) + throws JobPersistenceException { + + String lockName; + if(isAcquireTriggersWithinLock() || maxCount > 1) { + lockName = LOCK_TRIGGER_ACCESS; + } else { + lockName = null; + } + return executeInNonManagedTXLock(lockName, + new TransactionCallback>() { + public List execute(Connection conn) throws JobPersistenceException { + return acquireNextTrigger(conn, noLaterThan, maxCount, timeWindow); + } + }, + new TransactionValidator>() { + public Boolean validate(Connection conn, List result) throws JobPersistenceException { + try { + List acquired = getDelegate().selectInstancesFiredTriggerRecords(conn, getInstanceId()); + Set fireInstanceIds = new HashSet(); + for (FiredTriggerRecord ft : acquired) { + fireInstanceIds.add(ft.getFireInstanceId()); + } + for (OperableTrigger tr : result) { + if (fireInstanceIds.contains(tr.getFireInstanceId())) { + return true; + } + } + return false; + } catch (SQLException e) { + throw new JobPersistenceException("error validating trigger acquisition", e); + } + } + }); + } + + // FUTURE_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 { - Trigger nextTrigger = null; - - boolean acquiredOne = false; - + protected List acquireNextTrigger(Connection conn, long noLaterThan, int maxCount, long timeWindow) + throws JobPersistenceException { + if (timeWindow < 0) { + throw new IllegalArgumentException(); + } + + List acquiredTriggers = new ArrayList(); + Set acquiredJobKeysForNoConcurrentExec = new HashSet(); + final int MAX_DO_LOOP_RETRY = 3; + int currentLoopCount = 0; + long firstAcquiredTriggerFireTime = 0; + do { + currentLoopCount ++; try { - getDelegate().updateTriggerStateFromOtherStatesBeforeTime(conn, - STATE_MISFIRED, STATE_WAITING, STATE_WAITING, - getMisfireTime()); // only waiting + List keys = getDelegate().selectTriggerToAcquire(conn, noLaterThan + timeWindow, getMisfireTime(), maxCount); + + // No trigger is ready to fire yet. + if (keys == null || keys.size() == 0) + return acquiredTriggers; + + for(TriggerKey triggerKey: keys) { + // If our trigger is no longer available, try a new one. + OperableTrigger nextTrigger = retrieveTrigger(conn, triggerKey); + if(nextTrigger == null) { + continue; // next trigger + } + + // If trigger's job is set as @DisallowConcurrentExecution, and it has already been added to result, then + // put it back into the timeTriggers set and continue to search for next trigger. + JobKey jobKey = nextTrigger.getJobKey(); + JobDetail job = getDelegate().selectJobDetail(conn, jobKey, getClassLoadHelper()); + if (job.isConcurrentExectionDisallowed()) { + if (acquiredJobKeysForNoConcurrentExec.contains(jobKey)) { + continue; // next trigger + } else { + acquiredJobKeysForNoConcurrentExec.add(jobKey); + } + } + + // We now have a acquired trigger, let's add to return list. + // If our trigger was no longer in the expected state, try a new one. + int rowsUpdated = getDelegate().updateTriggerStateFromOtherState(conn, triggerKey, STATE_ACQUIRED, STATE_WAITING); + if (rowsUpdated <= 0) { + continue; // next trigger + } + nextTrigger.setFireInstanceId(getFiredTriggerRecordId()); + getDelegate().insertFiredTrigger(conn, nextTrigger, STATE_ACQUIRED, null); - long nextFireTime = getDelegate().selectNextFireTime(conn); + acquiredTriggers.add(nextTrigger); + if(firstAcquiredTriggerFireTime == 0) + firstAcquiredTriggerFireTime = nextTrigger.getNextFireTime().getTime(); + } - if (nextFireTime == 0 || nextFireTime > noLaterThan) - return null; - - Key triggerKey = null; - do { - triggerKey = getDelegate().selectTriggerForFireTime(conn, - nextFireTime); - if (null != triggerKey) { - - int res = getDelegate() - .updateTriggerStateFromOtherState(conn, - triggerKey.getName(), - triggerKey.getGroup(), STATE_ACQUIRED, - STATE_WAITING); - - if (res <= 0) continue; - - nextTrigger = retrieveTrigger(conn, ctxt, triggerKey - .getName(), triggerKey.getGroup()); - - if(nextTrigger == null) continue; - - nextTrigger - .setFireInstanceId(getFiredTriggerRecordId()); - getDelegate().insertFiredTrigger(conn, nextTrigger, - STATE_ACQUIRED, null); - - acquiredOne = true; - } - } while (triggerKey != null && !acquiredOne); + // if we didn't end up with any trigger to fire from that first + // batch, try again for another batch. We allow with a max retry count. + if(acquiredTriggers.size() == 0 && currentLoopCount < MAX_DO_LOOP_RETRY) { + continue; + } + + // We are done with the while loop. + break; } catch (Exception e) { throw new JobPersistenceException( - "Couldn't acquire next trigger: " + e.getMessage(), e); + "Couldn't acquire next trigger: " + e.getMessage(), e); } - - } while (!acquiredOne); - - return nextTrigger; + } while (true); + + // Return the acquired trigger list + return acquiredTriggers; } - + + /** + *

+ * Inform the JobStore that the scheduler no longer plans to + * fire the given Trigger, that it had previously acquired + * (reserved). + *

+ */ + public void releaseAcquiredTrigger(final OperableTrigger trigger) { + retryExecuteInNonManagedTXLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + releaseAcquiredTrigger(conn, trigger); + } + }); + } + protected void releaseAcquiredTrigger(Connection conn, - SchedulingContext ctxt, Trigger trigger) - throws JobPersistenceException { + OperableTrigger trigger) + throws JobPersistenceException { try { getDelegate().updateTriggerStateFromOtherState(conn, - trigger.getName(), trigger.getGroup(), STATE_WAITING, - STATE_ACQUIRED); + trigger.getKey(), STATE_WAITING, STATE_ACQUIRED); getDelegate().deleteFiredTrigger(conn, trigger.getFireInstanceId()); } catch (SQLException e) { throw new JobPersistenceException( "Couldn't release acquired trigger: " + e.getMessage(), e); } } + /** + *

+ * Inform the JobStore that the scheduler is now firing the + * given Trigger (executing its associated Job), + * that it had previously acquired (reserved). + *

+ * + * @return null if the trigger or its job or calendar no longer exist, or + * if the trigger was not successfully put into the 'executing' + * state. + */ + @SuppressWarnings("unchecked") + public List triggersFired(final List triggers) throws JobPersistenceException { + return executeInNonManagedTXLock(LOCK_TRIGGER_ACCESS, + new TransactionCallback>() { + public List execute(Connection conn) throws JobPersistenceException { + List results = new ArrayList(); + + TriggerFiredResult result; + for (OperableTrigger trigger : triggers) { + try { + TriggerFiredBundle bundle = triggerFired(conn, trigger); + result = new TriggerFiredResult(bundle); + } catch (JobPersistenceException jpe) { + result = new TriggerFiredResult(jpe); + } catch(RuntimeException re) { + result = new TriggerFiredResult(re); + } + results.add(result); + } + + return results; + } + }, + new TransactionValidator>() { + @Override + public Boolean validate(Connection conn, List result) throws JobPersistenceException { + try { + List acquired = getDelegate().selectInstancesFiredTriggerRecords(conn, getInstanceId()); + Set executingTriggers = new HashSet(); + for (FiredTriggerRecord ft : acquired) { + if (STATE_EXECUTING.equals(ft.getFireInstanceState())) { + executingTriggers.add(ft.getFireInstanceId()); + } + } + for (TriggerFiredResult tr : result) { + if (tr.getTriggerFiredBundle() != null && executingTriggers.contains(tr.getTriggerFiredBundle().getTrigger().getFireInstanceId())) { + return true; + } + } + return false; + } catch (SQLException e) { + throw new JobPersistenceException("error validating trigger acquisition", e); + } + } + }); + } + protected TriggerFiredBundle triggerFired(Connection conn, - SchedulingContext ctxt, Trigger trigger) - throws JobPersistenceException { - JobDetail job = null; + OperableTrigger trigger) + throws JobPersistenceException { + JobDetail job; Calendar cal = null; // Make sure trigger wasn't deleted, paused, or completed... try { // if trigger was deleted, state will be STATE_DELETED String state = getDelegate().selectTriggerState(conn, - trigger.getName(), trigger.getGroup()); - if (!state.equals(STATE_ACQUIRED)) return null; + trigger.getKey()); + if (!state.equals(STATE_ACQUIRED)) { + return null; + } } catch (SQLException e) { throw new JobPersistenceException("Couldn't select trigger state: " + e.getMessage(), e); } try { - job = retrieveJob(conn, ctxt, trigger.getJobName(), trigger - .getJobGroup()); + job = retrieveJob(conn, trigger.getJobKey()); if (job == null) { return null; } } catch (JobPersistenceException jpe) { try { getLog().error("Error retrieving job, setting trigger state to ERROR.", jpe); - getDelegate().updateTriggerState(conn, trigger.getName(), - trigger.getGroup(), STATE_ERROR); + getDelegate().updateTriggerState(conn, trigger.getKey(), + STATE_ERROR); } catch (SQLException sqle) { getLog().error("Unable to set trigger state to ERROR.", sqle); } throw jpe; } if (trigger.getCalendarName() != null) { - cal = retrieveCalendar(conn, ctxt, trigger.getCalendarName()); + cal = retrieveCalendar(conn, trigger.getCalendarName()); if (cal == null) { return null; } } try { - getDelegate().deleteFiredTrigger(conn, trigger.getFireInstanceId()); - getDelegate().insertFiredTrigger(conn, trigger, STATE_EXECUTING, - job); + getDelegate().updateFiredTrigger(conn, trigger, STATE_EXECUTING, job); } catch (SQLException e) { throw new JobPersistenceException("Couldn't insert fired trigger: " + e.getMessage(), e); @@ -1851,16 +2994,16 @@ String state = STATE_WAITING; boolean force = true; - if (job.isStateful()) { + if (job.isConcurrentExectionDisallowed()) { state = STATE_BLOCKED; force = false; try { - getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getName(), - job.getGroup(), STATE_BLOCKED, STATE_WAITING); - getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getName(), - job.getGroup(), STATE_BLOCKED, STATE_ACQUIRED); - getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getName(), - job.getGroup(), STATE_PAUSED_BLOCKED, STATE_PAUSED); + getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getKey(), + STATE_BLOCKED, STATE_WAITING); + getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getKey(), + STATE_BLOCKED, STATE_ACQUIRED); + getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getKey(), + STATE_PAUSED_BLOCKED, STATE_PAUSED); } catch (SQLException e) { throw new JobPersistenceException( "Couldn't update states of blocked triggers: " @@ -1869,67 +3012,92 @@ } if (trigger.getNextFireTime() == null) { - state = STATE_COMPLETE; - force = true; + state = STATE_COMPLETE; + force = true; } - storeTrigger(conn, ctxt, trigger, job, true, state, force, false); + storeTrigger(conn, trigger, job, true, state, force, false); job.getJobDataMap().clearDirtyFlag(); - return new TriggerFiredBundle(job, trigger, cal, trigger.getGroup() + return new TriggerFiredBundle(job, trigger, cal, trigger.getKey().getGroup() .equals(Scheduler.DEFAULT_RECOVERY_GROUP), new Date(), trigger .getPreviousFireTime(), prevFireTime, trigger.getNextFireTime()); } + /** + *

+ * Inform the JobStore that the scheduler has completed the + * firing of the given Trigger (and the execution its + * associated Job), and that the {@link org.quartz.JobDataMap} + * in the given JobDetail should be updated if the Job + * is stateful. + *

+ */ + public void triggeredJobComplete(final OperableTrigger trigger, + final JobDetail jobDetail, final CompletedExecutionInstruction triggerInstCode) { + retryExecuteInNonManagedTXLock( + LOCK_TRIGGER_ACCESS, + new VoidTransactionCallback() { + public void executeVoid(Connection conn) throws JobPersistenceException { + triggeredJobComplete(conn, trigger, jobDetail,triggerInstCode); + } + }); + } + protected void triggeredJobComplete(Connection conn, - SchedulingContext ctxt, Trigger trigger, JobDetail jobDetail, - int triggerInstCode) throws JobPersistenceException { + OperableTrigger trigger, JobDetail jobDetail, + CompletedExecutionInstruction triggerInstCode) throws JobPersistenceException { try { - if (triggerInstCode == Trigger.INSTRUCTION_DELETE_TRIGGER) { + if (triggerInstCode == CompletedExecutionInstruction.DELETE_TRIGGER) { if(trigger.getNextFireTime() == null) { // double check for possible reschedule within job // execution, which would cancel the need to delete... TriggerStatus stat = getDelegate().selectTriggerStatus( - conn, trigger.getName(), trigger.getGroup()); + conn, trigger.getKey()); if(stat != null && stat.getNextFireTime() == null) { - removeTrigger(conn, ctxt, trigger.getName(), trigger.getGroup()); + removeTrigger(conn, trigger.getKey()); } + } else{ + removeTrigger(conn, trigger.getKey()); + signalSchedulingChangeOnTxCompletion(0L); } - else{ - removeTrigger(conn, ctxt, trigger.getName(), trigger.getGroup()); - } - } else if (triggerInstCode == Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE) { - getDelegate().updateTriggerState(conn, trigger.getName(), - trigger.getGroup(), STATE_COMPLETE); - } else if (triggerInstCode == Trigger.INSTRUCTION_SET_TRIGGER_ERROR) { - getLog().info("Trigger " + trigger.getFullName() + " set to ERROR state."); - getDelegate().updateTriggerState(conn, trigger.getName(), - trigger.getGroup(), STATE_ERROR); - } else if (triggerInstCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE) { - getDelegate().updateTriggerStatesForJob(conn, - trigger.getJobName(), trigger.getJobGroup(), + } else if (triggerInstCode == CompletedExecutionInstruction.SET_TRIGGER_COMPLETE) { + getDelegate().updateTriggerState(conn, trigger.getKey(), STATE_COMPLETE); - } else if (triggerInstCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR) { + signalSchedulingChangeOnTxCompletion(0L); + } else if (triggerInstCode == CompletedExecutionInstruction.SET_TRIGGER_ERROR) { + getLog().info("Trigger " + trigger.getKey() + " set to ERROR state."); + getDelegate().updateTriggerState(conn, trigger.getKey(), + STATE_ERROR); + signalSchedulingChangeOnTxCompletion(0L); + } else if (triggerInstCode == CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_COMPLETE) { + getDelegate().updateTriggerStatesForJob(conn, + trigger.getJobKey(), STATE_COMPLETE); + signalSchedulingChangeOnTxCompletion(0L); + } else if (triggerInstCode == CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR) { getLog().info("All triggers of Job " + - trigger.getFullJobName() + " set to ERROR state."); + trigger.getKey() + " set to ERROR state."); getDelegate().updateTriggerStatesForJob(conn, - trigger.getJobName(), trigger.getJobGroup(), - STATE_ERROR); + trigger.getJobKey(), STATE_ERROR); + signalSchedulingChangeOnTxCompletion(0L); } - if (jobDetail.isStateful()) { + if (jobDetail.isConcurrentExectionDisallowed()) { getDelegate().updateTriggerStatesForJobFromOtherState(conn, - jobDetail.getName(), jobDetail.getGroup(), - STATE_WAITING, STATE_BLOCKED); + jobDetail.getKey(), STATE_WAITING, + STATE_BLOCKED); getDelegate().updateTriggerStatesForJobFromOtherState(conn, - jobDetail.getName(), jobDetail.getGroup(), - STATE_PAUSED, STATE_PAUSED_BLOCKED); + jobDetail.getKey(), STATE_PAUSED, + STATE_PAUSED_BLOCKED); + signalSchedulingChangeOnTxCompletion(0L); + } + if (jobDetail.isPersistJobDataAfterExecution()) { try { if (jobDetail.getJobDataMap().isDirty()) { - getDelegate().updateJobData(conn, jobDetail); + getDelegate().updateJobData(conn, jobDetail); } } catch (IOException e) { throw new JobPersistenceException( @@ -1958,172 +3126,280 @@ *

*/ 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}; - } + synchronized(this) { + if(null == delegate) { + try { + if(delegateClassName != null) { + delegateClass = getClassLoadHelper().loadClass(delegateClassName, DriverDelegate.class); + } - 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()); + delegate = delegateClass.newInstance(); + + delegate.initialize(getLog(), tablePrefix, instanceName, instanceId, getClassLoadHelper(), canUseProperties(), getDriverDelegateInitString()); + + } catch (InstantiationException e) { + throw new NoSuchDelegateException("Couldn't create delegate: " + + e.getMessage(), e); + } catch (IllegalAccessException e) { + throw new NoSuchDelegateException("Couldn't create delegate: " + + e.getMessage(), e); + } catch (ClassNotFoundException e) { + throw new NoSuchDelegateException("Couldn't load delegate class: " + + e.getMessage(), e); + } } + return delegate; } - - return delegate; } protected Semaphore getLockHandler() { return lockHandler; } + public void setLockHandler(Semaphore lockHandler) { + this.lockHandler = lockHandler; + } + //--------------------------------------------------------------------------- // Management methods //--------------------------------------------------------------------------- - protected abstract boolean doRecoverMisfires() - throws JobPersistenceException; + 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().countMisfiredTriggersInState( + conn, 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(LOCK_TRIGGER_ACCESS, transOwner); + } finally { + cleanupConnection(conn); + } + } + } - protected void signalSchedulingChange() { - signaler.signalSchedulingChange(); + protected ThreadLocal sigChangeForTxCompletion = new ThreadLocal(); + protected void signalSchedulingChangeOnTxCompletion(long candidateNewNextFireTime) { + Long sigTime = sigChangeForTxCompletion.get(); + if(sigTime == null && candidateNewNextFireTime >= 0L) + sigChangeForTxCompletion.set(candidateNewNextFireTime); + else { + if(sigTime == null || candidateNewNextFireTime < sigTime) + sigChangeForTxCompletion.set(candidateNewNextFireTime); + } } + + protected Long clearAndGetSignalSchedulingChangeOnTxCompletion() { + Long t = sigChangeForTxCompletion.get(); + sigChangeForTxCompletion.set(null); + return t; + } + protected void signalSchedulingChangeImmediately(long candidateNewNextFireTime) { + schedSignaler.signalSchedulingChange(candidateNewNextFireTime); + } + //--------------------------------------------------------------------------- // Cluster management methods //--------------------------------------------------------------------------- - protected abstract boolean doCheckin() throws JobPersistenceException; - 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 acquire 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) { + failedRecords = clusterCheckIn(conn); + commitConnection(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(LOCK_TRIGGER_ACCESS, transOwner); + } finally { + try { + releaseLock(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 has no recoverer and is checking for the - * first time. + * This includes this scheduler if it is checking in for the first time. */ - protected List findFailedInstances(Connection conn) - throws JobPersistenceException { - - List failedInstances = new LinkedList(); - boolean selfFailed = false; - - long timeNow = System.currentTimeMillis(); - + protected List findFailedInstances(Connection conn) + throws JobPersistenceException { try { - List states = getDelegate().selectSchedulerStateRecords(conn, null); - // build map of states by Id... - HashMap statesById = new HashMap(); - Iterator itr = states.iterator(); - while (itr.hasNext()) { - SchedulerStateRecord rec = (SchedulerStateRecord) itr.next(); - statesById.put(rec.getSchedulerInstanceId(), rec); - } + List failedInstances = new LinkedList(); + boolean foundThisScheduler = false; + long timeNow = System.currentTimeMillis(); + + List states = getDelegate().selectSchedulerStateRecords(conn, null); - itr = states.iterator(); - while (itr.hasNext()) { - SchedulerStateRecord rec = (SchedulerStateRecord) itr.next(); + for(SchedulerStateRecord rec: states) { // find own record... if (rec.getSchedulerInstanceId().equals(getInstanceId())) { + foundThisScheduler = true; if (firstCheckIn) { - if (rec.getRecoverer() == null) { - failedInstances.add(rec); - } else { - // make sure the recoverer hasn't died itself! - SchedulerStateRecord recOrec = (SchedulerStateRecord)statesById.get(rec.getRecoverer()); - - long failedIfAfter = (recOrec == null) ? timeNow : calcFailedIfAfter(recOrec); - - // if it has failed, then let's become the recoverer - if( failedIfAfter < timeNow || recOrec == null) { - failedInstances.add(rec); - } - } + failedInstances.add(rec); } - // TODO: revisit when handle self-failed-out impled (see TODO in clusterCheckIn() below) - // else if (rec.getRecoverer() != null) { - // selfFailed = true; - // } } else { // find failed instances... - long failedIfAfter = calcFailedIfAfter(rec); - - if (rec.getRecoverer() == null) { - if (failedIfAfter < timeNow) { - failedInstances.add(rec); - } - } else { - // make sure the recoverer hasn't died itself! - SchedulerStateRecord recOrec = (SchedulerStateRecord)statesById.get(rec.getRecoverer()); - - failedIfAfter = (recOrec == null) ? timeNow : calcFailedIfAfter(recOrec); - - // if it has failed, then let's become the recoverer - if (failedIfAfter < timeNow || recOrec == null) { - failedInstances.add(rec); - } + 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) && (!firstCheckIn)) { + // FUTURE_TODO: revisit when handle self-failed-out impl'ed (see FUTURE_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); } + } - return failedInstances; + /** + * Create dummy SchedulerStateRecord 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()) { + for (SchedulerStateRecord rec: schedulerStateRecords) { + + allFiredTriggerInstanceNames.remove(rec.getSchedulerInstanceId()); + } + + for (String inst: allFiredTriggerInstanceNames) { + + SchedulerStateRecord orphanedInstance = new SchedulerStateRecord(); + orphanedInstance.setSchedulerInstanceId(inst); + + 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; + return rec.getCheckinTimestamp() + + Math.max(rec.getCheckinInterval(), + (System.currentTimeMillis() - lastCheckin)) + + 7500L; } - protected List clusterCheckIn(Connection conn) - throws JobPersistenceException { + protected List clusterCheckIn(Connection conn) + throws JobPersistenceException { - List failedInstances = findFailedInstances(conn); + List failedInstances = findFailedInstances(conn); try { - // TODO: handle self-failed-out + // FUTURE_TODO: handle self-failed-out // check in... lastCheckin = System.currentTimeMillis(); - if(getDelegate().updateSchedulerState(conn, getInstanceId(), lastCheckin, null) == 0) { + if(getDelegate().updateSchedulerState(conn, getInstanceId(), lastCheckin) == 0) { getDelegate().insertSchedulerState(conn, getInstanceId(), - lastCheckin, getClusterCheckinInterval(), null); + lastCheckin, getClusterCheckinInterval()); } } catch (Exception e) { @@ -2134,8 +3410,9 @@ return failedInstances; } - protected void clusterRecover(Connection conn, List failedInstances) - throws JobPersistenceException { + @SuppressWarnings("ConstantConditions") + protected void clusterRecover(Connection conn, List failedInstances) + throws JobPersistenceException { if (failedInstances.size() > 0) { @@ -2145,77 +3422,73 @@ "ClusterManager: detected " + failedInstances.size() + " failed or restarted instances."); try { - Iterator itr = failedInstances.iterator(); - while (itr.hasNext()) { - SchedulerStateRecord rec = (SchedulerStateRecord) itr - .next(); - + for (SchedulerStateRecord rec : failedInstances) { getLog().info( "ClusterManager: Scanning for instance \"" + rec.getSchedulerInstanceId() + "\"'s failed in-progress jobs."); - List firedTriggerRecs = getDelegate() + List firedTriggerRecs = getDelegate() .selectInstancesFiredTriggerRecords(conn, rec.getSchedulerInstanceId()); int acquiredCount = 0; int recoveredCount = 0; int otherCount = 0; - Iterator ftItr = firedTriggerRecs.iterator(); - while (ftItr.hasNext()) { - FiredTriggerRecord ftRec = (FiredTriggerRecord) ftItr - .next(); + Set triggerKeys = new HashSet(); - Key tKey = ftRec.getTriggerKey(); - Key jKey = ftRec.getJobKey(); + for (FiredTriggerRecord ftRec : firedTriggerRecs) { + TriggerKey tKey = ftRec.getTriggerKey(); + JobKey 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); - } - if (ftRec.getFireInstanceState().equals(STATE_PAUSED_BLOCKED)) { + conn, jKey, + STATE_WAITING, STATE_BLOCKED); + } else if (ftRec.getFireInstanceState().equals(STATE_PAUSED_BLOCKED)) { getDelegate() .updateTriggerStatesForJobFromOtherState( - conn, jKey.getName(), - jKey.getGroup(), STATE_PAUSED, - STATE_PAUSED_BLOCKED); + conn, jKey, + 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); + conn, tKey, STATE_WAITING, + STATE_ACQUIRED); acquiredCount++; - }// handle jobs marked for recovery that were not fully - // executed.. - else if (ftRec.isJobRequestsRecovery()) { - if (jobExists(conn, jKey.getName(), jKey.getGroup())) { - SimpleTrigger rcvryTrig = new SimpleTrigger( + } else if (ftRec.isJobRequestsRecovery()) { + // handle jobs marked for recovery that were not fully + // executed.. + if (jobExists(conn, jKey)) { + @SuppressWarnings("deprecation") + SimpleTriggerImpl rcvryTrig = new SimpleTriggerImpl( "recover_" + rec.getSchedulerInstanceId() + "_" + String.valueOf(recoverIds++), Scheduler.DEFAULT_RECOVERY_GROUP, - new Date(ftRec.getFireTimestamp())); + new Date(ftRec.getScheduleTimestamp())); rcvryTrig.setJobName(jKey.getName()); rcvryTrig.setJobGroup(jKey.getGroup()); - rcvryTrig - .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW); + rcvryTrig.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY); + rcvryTrig.setPriority(ftRec.getPriority()); JobDataMap jd = getDelegate().selectTriggerJobDataMap(conn, tKey.getName(), tKey.getGroup()); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_NAME", tKey.getName()); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_GROUP", tKey.getGroup()); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_FIRETIME_IN_MILLISECONDS_AS_STRING", String.valueOf(ftRec.getFireTimestamp())); + 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())); + jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_SCHEDULED_FIRETIME_IN_MILLISECONDS, String.valueOf(ftRec.getScheduleTimestamp())); rcvryTrig.setJobDataMap(jd); rcvryTrig.computeFirstFireTime(null); - storeTrigger(conn, null, rcvryTrig, null, false, + storeTrigger(conn, rcvryTrig, null, false, STATE_WAITING, false, true); recoveredCount++; } else { @@ -2231,96 +3504,157 @@ } // free up stateful job's triggers - if (ftRec.isJobIsStateful()) { + if (ftRec.isJobDisallowsConcurrentExecution()) { getDelegate() - .updateTriggerStatesForJobFromOtherState( - conn, jKey.getName(), - jKey.getGroup(), STATE_WAITING, - STATE_BLOCKED); + .updateTriggerStatesForJobFromOtherState( + conn, jKey, + STATE_WAITING, STATE_BLOCKED); getDelegate() - .updateTriggerStatesForJobFromOtherState( - conn, jKey.getName(), - jKey.getGroup(), STATE_PAUSED, - STATE_PAUSED_BLOCKED); + .updateTriggerStatesForJobFromOtherState( + conn, jKey, + 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 (TriggerKey triggerKey : triggerKeys) { + + if (getDelegate().selectTriggerState(conn, triggerKey). + equals(STATE_COMPLETE)) { + List firedTriggers = + getDelegate().selectFiredTriggerRecords(conn, triggerKey.getName(), triggerKey.getGroup()); + if (firedTriggers.isEmpty()) { + + if (removeTrigger(conn, triggerKey)) { + 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)."); - getDelegate().deleteSchedulerState(conn, - rec.getSchedulerInstanceId()); - - // update record to show that recovery was handled - if (rec.getSchedulerInstanceId().equals(getInstanceId())) { - getDelegate().insertSchedulerState(conn, - rec.getSchedulerInstanceId(), System.currentTimeMillis(), - rec.getCheckinInterval(), null); + if (!rec.getSchedulerInstanceId().equals(getInstanceId())) { + getDelegate().deleteSchedulerState(conn, + rec.getSchedulerInstanceId()); } - } - } catch (Exception e) { + } catch (Throwable e) { throw new JobPersistenceException("Failure recovering jobs: " + e.getMessage(), e); } } } protected void logWarnIfNonZero(int val, String warning) { - if (val > 0) getLog().info(warning); - else + if (val > 0) { + getLog().info(warning); + } else { getLog().debug(warning); + } } /** - * Closes the supplied connection - * - * @param conn (Optional) - * @throws JobPersistenceException thrown if a SQLException occurs when the - * connection is closed + *

+ * 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 closeConnection(Connection conn) - throws JobPersistenceException { - + 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 supplied Connection. + *

+ * Ignores a null Connection. + * Any exception thrown trying to close the Connection is + * logged and ignored. + *

+ * + * @param conn The Connection to close (Optional). + */ + protected void closeConnection(Connection conn) { + if (conn != null) { try { conn.close(); } catch (SQLException e) { - throw new JobPersistenceException( - "Couldn't close jdbc connection. "+e.getMessage(), 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 + * 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) - * @throws JobPersistenceException thrown if a SQLException occurs when the - * connection is rolled back */ - protected void rollbackConnection(Connection conn) - throws JobPersistenceException { - + protected void rollbackConnection(Connection conn) { if (conn != null) { try { conn.rollback(); } catch (SQLException e) { - throw new JobPersistenceException( + getLog().error( "Couldn't rollback jdbc connection. "+e.getMessage(), e); } } } + /** * Commit the supplied connection * @@ -2340,7 +3674,153 @@ } } } + + /** + * 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 { + T execute(Connection conn) throws JobPersistenceException; + } + protected interface TransactionValidator { + Boolean validate(Connection conn, T result) 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 abstract class VoidTransactionCallback implements TransactionCallback { + public final Void execute(Connection conn) throws JobPersistenceException { + executeVoid(conn); + return null; + } + + abstract void executeVoid(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 T executeWithoutLock( + TransactionCallback txCallback) throws JobPersistenceException { + return executeInLock(null, txCallback); + } + + /** + * Execute the given callback having acquired 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 acquire, for example + * "TRIGGER_ACCESS". If null, then no lock is acquired, but the + * lockCallback is still executed in a transaction. + */ + protected abstract T executeInLock( + String lockName, + TransactionCallback txCallback) throws JobPersistenceException; + + protected T retryExecuteInNonManagedTXLock(String lockName, TransactionCallback txCallback) { + for (int retry = 1; !shutdown; retry++) { + try { + return executeInNonManagedTXLock(lockName, txCallback, null); + } catch (JobPersistenceException jpe) { + if(retry % 4 == 0) { + schedSignaler.notifySchedulerListenersError("An error occurred while " + txCallback, jpe); + } + } catch (RuntimeException e) { + getLog().error("retryExecuteInNonManagedTXLock: RuntimeException " + e.getMessage(), e); + } + try { + Thread.sleep(getDbRetryInterval()); // retry every N seconds (the db connection must be failed) + } catch (InterruptedException e) { + throw new IllegalStateException("Received interrupted exception", e); + } + } + throw new IllegalStateException("JobStore is shutdown - aborting retry"); + } + + /** + * Execute the given callback having optionally acquired the given lock. + * This uses the non-managed transaction connection. + * + * @param lockName The name of the lock to acquire, for example + * "TRIGGER_ACCESS". If null, then no lock is acquired, but the + * lockCallback is still executed in a non-managed transaction. + */ + protected T executeInNonManagedTXLock( + String lockName, + TransactionCallback txCallback, final TransactionValidator txValidator) 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 acquiring the lock since it isn't needed. + if (getLockHandler().requiresConnection()) { + conn = getNonManagedTXConnection(); + } + + transOwner = getLockHandler().obtainLock(conn, lockName); + } + + if (conn == null) { + conn = getNonManagedTXConnection(); + } + + final T result = txCallback.execute(conn); + try { + commitConnection(conn); + } catch (JobPersistenceException e) { + rollbackConnection(conn); + if (txValidator == null || !retryExecuteInNonManagedTXLock(lockName, new TransactionCallback() { + @Override + public Boolean execute(Connection conn) throws JobPersistenceException { + return txValidator.validate(conn, result); + } + })) { + throw e; + } + } + + Long sigTime = clearAndGetSignalSchedulingChangeOnTxCompletion(); + if(sigTime != null && sigTime >= 0) { + signalSchedulingChangeImmediately(sigTime); + } + + 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(lockName, transOwner); + } finally { + cleanupConnection(conn); + } + } + } + ///////////////////////////////////////////////////////////////////////////// // // ClusterManager Thread @@ -2349,21 +3829,21 @@ class ClusterManager extends Thread { - private boolean shutdown = false; + private volatile boolean shutdown = false; - private JobStoreSupport js; - private int numFails = 0; - ClusterManager(JobStoreSupport js) { - this.js = js; + ClusterManager() { this.setPriority(Thread.NORM_PRIORITY + 2); this.setName("QuartzScheduler_" + instanceName + "-" + instanceId + "_ClusterManager"); + this.setDaemon(getMakeThreadsDaemons()); } public void initialize() { this.manage(); - this.start(); + + ThreadExecutor executor = getThreadExecutor(); + executor.execute(ClusterManager.this); } public void shutdown() { @@ -2375,38 +3855,46 @@ boolean res = false; try { - res = js.doCheckin(); + res = doCheckin(); numFails = 0; getLog().debug("ClusterManager: Check-in complete."); } catch (Exception e) { - if(numFails % 4 == 0) + if(numFails % 4 == 0) { getLog().error( "ClusterManager: Error managing cluster: " + e.getMessage(), e); + } numFails++; } return res; } + @Override public void run() { while (!shutdown) { if (!shutdown) { long timeToSleep = getClusterCheckinInterval(); long transpiredTime = (System.currentTimeMillis() - lastCheckin); timeToSleep = timeToSleep - transpiredTime; - if (timeToSleep <= 0) timeToSleep = 100L; + if (timeToSleep <= 0) { + timeToSleep = 100L; + } - if(numFails > 0) timeToSleep = Math.max(getDbRetryInterval(), timeToSleep); + if(numFails > 0) { + timeToSleep = Math.max(getDbRetryInterval(), timeToSleep); + } try { Thread.sleep(timeToSleep); } catch (Exception ignore) { } } - if (!shutdown && this.manage()) signalSchedulingChange(); + if (!shutdown && this.manage()) { + signalSchedulingChangeImmediately(0L); + } }//while !shutdown } @@ -2420,79 +3908,78 @@ class MisfireHandler extends Thread { - private boolean shutdown = false; + private volatile boolean shutdown = false; - private JobStoreSupport js; - private int numFails = 0; - MisfireHandler(JobStoreSupport js) { - this.js = js; + MisfireHandler() { this.setName("QuartzScheduler_" + instanceName + "-" + instanceId + "_MisfireHandler"); + this.setDaemon(getMakeThreadsDaemons()); } public void initialize() { - //this.manage(); - this.start(); + ThreadExecutor executor = getThreadExecutor(); + executor.execute(MisfireHandler.this); } public void shutdown() { shutdown = true; this.interrupt(); } - private boolean manage() { + private RecoverMisfiredJobsResult manage() { try { getLog().debug("MisfireHandler: scanning for misfires..."); - boolean res = js.doRecoverMisfires(); + RecoverMisfiredJobsResult res = doRecoverMisfires(); numFails = 0; return res; } catch (Exception e) { - if(numFails % 4 == 0) + if(numFails % 4 == 0) { getLog().error( "MisfireHandler: Error handling misfires: " + e.getMessage(), e); + } numFails++; } - return false; + return RecoverMisfiredJobsResult.NO_OP; } + @Override public void run() { while (!shutdown) { long sTime = System.currentTimeMillis(); - boolean moreToDo = this.manage(); + RecoverMisfiredJobsResult recoverMisfiredJobsResult = manage(); - if (lastRecoverCount > 0) signalSchedulingChange(); + if (recoverMisfiredJobsResult.getProcessedMisfiredTriggerCount() > 0) { + signalSchedulingChangeImmediately(recoverMisfiredJobsResult.getEarliestNewTime()); + } - long spanTime = System.currentTimeMillis() - sTime; + 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 (!shutdown && !moreToDo) { - long timeToSleep = getMisfireThreshold() - spanTime; - if (timeToSleep <= 0) timeToSleep = 50L; - - if(numFails > 0) timeToSleep = Math.max(getDbRetryInterval(), timeToSleep); + if(numFails > 0) { + timeToSleep = Math.max(getDbRetryInterval(), timeToSleep); + } + } - if (timeToSleep > 0) try { + try { Thread.sleep(timeToSleep); } catch (Exception ignore) { } - } - else if(moreToDo) { // short pause to help balance threads... - try { - Thread.sleep(50); - } catch (Exception ignore) { - } - } - - }//while !shutdown + }//while !shutdown + } } } - } // EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JobStoreTX.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JobStoreTX.java (.../JobStoreTX.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/JobStoreTX.java (.../JobStoreTX.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,27 +15,15 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; -import org.quartz.Calendar; -import org.quartz.JobDetail; +import java.sql.Connection; + import org.quartz.JobPersistenceException; -import org.quartz.ObjectAlreadyExistsException; import org.quartz.SchedulerConfigException; -import org.quartz.SchedulerException; -import org.quartz.Trigger; -import org.quartz.core.SchedulingContext; import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.SchedulerSignaler; -import org.quartz.spi.TriggerFiredBundle; -import java.sql.Connection; -import java.util.List; -import java.util.Set; - /** *

* JobStoreTX is meant to be used in a standalone environment. @@ -62,1363 +50,47 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - public void initialize(ClassLoadHelper loadHelper, - SchedulerSignaler signaler) throws SchedulerConfigException { + @Override + public void initialize(ClassLoadHelper classLoadHelper, + SchedulerSignaler schedSignaler) throws SchedulerConfigException { - super.initialize(loadHelper, signaler); + super.initialize(classLoadHelper, schedSignaler); getLog().info("JobStoreTX initialized."); } - //--------------------------------------------------------------------------- - // JobStoreSupport methods - //--------------------------------------------------------------------------- - /** - *

- * Recover any failed or misfired jobs and clean up the data store as - * appropriate. - *

+ * For JobStoreTX, the non-managed TX connection is just + * the normal connection because it is not CMT. * - * @throws JobPersistenceException - * if jobs could not be recovered + * @see JobStoreSupport#getConnection() */ - protected void recoverJobs() throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - recoverJobs(conn); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - protected void cleanVolatileTriggerAndJobs() throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - cleanVolatileTriggerAndJobs(conn); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - //--------------------------------------------------------------------------- - // job / trigger storage methods - //--------------------------------------------------------------------------- - - /** - *

- * Store the given {@link org.quartz.JobDetail} and {@link org.quartz.Trigger}. - *

- * - * @param newJob - * The 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 ObjectAlreadyExistsException, - JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - if(isLockOnInsert()) { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - } - - 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); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Store the given {@link org.quartz.JobDetail}. - *

- * - * @param newJob - * The 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(SchedulingContext ctxt, JobDetail newJob, - boolean replaceExisting) throws ObjectAlreadyExistsException, - JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - if(isLockOnInsert() || replaceExisting) { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - } - - storeJob(conn, ctxt, newJob, replaceExisting); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Remove (delete) the {@link org.quartz.Job} with the given - * name, and any {@link 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. - *

- * - * @param jobName - * The name of the 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) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - boolean removed = removeJob(conn, ctxt, jobName, groupName, true); - commitConnection(conn); - return removed; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Retrieve the {@link org.quartz.JobDetail} for the given - * {@link org.quartz.Job}. - *

- * - * @param jobName - * The name of the 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) throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - JobDetail job = retrieveJob(conn, ctxt, jobName, groupName); - commitConnection(conn); - return job; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - /** - *

- * Store the given {@link org.quartz.Trigger}. - *

- * - * @param newTrigger - * The 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(SchedulingContext ctxt, Trigger newTrigger, - boolean replaceExisting) throws ObjectAlreadyExistsException, - JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - if(isLockOnInsert() || replaceExisting) { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - } - - storeTrigger(conn, ctxt, newTrigger, null, replaceExisting, - STATE_WAITING, false, false); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Remove (delete) the {@link 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. - *

- * - * @param triggerName - * The name of the 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) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - boolean removed = removeTrigger(conn, ctxt, triggerName, groupName); - commitConnection(conn); - return removed; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - * @see org.quartz.spi.JobStore#replaceTrigger(org.quartz.core.SchedulingContext, java.lang.String, java.lang.String, org.quartz.Trigger) - */ - public boolean replaceTrigger(SchedulingContext ctxt, String triggerName, String groupName, Trigger newTrigger) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - boolean removed = replaceTrigger(conn, ctxt, triggerName, groupName, newTrigger); - commitConnection(conn); - return removed; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - - - /** - *

- * Retrieve the given {@link org.quartz.Trigger}. - *

- * - * @param triggerName - * The name of the 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) throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - Trigger trigger = retrieveTrigger(conn, ctxt, triggerName, - groupName); - commitConnection(conn); - return trigger; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - /** - *

- * Store the given {@link org.quartz.Calendar}. - *

- * - * @param calName - * The name of the calendar. - * @param calendar - * The 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(SchedulingContext ctxt, String calName, - Calendar calendar, boolean replaceExisting, boolean updateTriggers) - throws ObjectAlreadyExistsException, JobPersistenceException { - Connection conn = getConnection(); - boolean lockOwner = false; - try { - if(isLockOnInsert() || updateTriggers) { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - lockOwner = true; - } - - storeCalendar(conn, ctxt, calName, calendar, replaceExisting, updateTriggers); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, lockOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Remove (delete) the {@link org.quartz.Calendar} with the - * given name. - *

- * - *

- * If removal of the Calendar would result in - * s pointing to non-existent calendars, then a - * JobPersistenceException will be thrown.

- * * - * @param calName The name of the 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 { - Connection conn = getConnection(); - boolean lockOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - lockOwner = true; - - boolean removed = removeCalendar(conn, ctxt, calName); - commitConnection(conn); - return removed; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, lockOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Retrieve the given {@link org.quartz.Trigger}. - *

- * - * @param calName - * The name of the Calendar to be retrieved. - * @return The desired Calendar, or null if there is no - * match. - */ - public Calendar retrieveCalendar(SchedulingContext ctxt, String calName) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - Calendar cal = retrieveCalendar(conn, ctxt, calName); - commitConnection(conn); - return cal; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - //--------------------------------------------------------------------------- - // informational methods - //--------------------------------------------------------------------------- - - /** - *

- * Get the number of {@link org.quartz.Job} s that are - * stored in the JobStore. - *

- */ - public int getNumberOfJobs(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - int numJobs = getNumberOfJobs(conn, ctxt); - commitConnection(conn); - return numJobs; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the number of {@link org.quartz.Trigger} s that are - * stored in the JobsStore. - *

- */ - public int getNumberOfTriggers(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - int numTriggers = getNumberOfTriggers(conn, ctxt); - commitConnection(conn); - return numTriggers; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the number of {@link org.quartz.Calendar} s that are - * stored in the JobsStore. - *

- */ - public int getNumberOfCalendars(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - int numCals = getNumberOfCalendars(conn, ctxt); - commitConnection(conn); - return numCals; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - - public Set getPausedTriggerGroups(SchedulingContext ctxt) + @Override + protected Connection getNonManagedTXConnection() throws JobPersistenceException { - - Connection conn = getConnection(); - try { - // no locks necessary for read... - Set groups = getPausedTriggerGroups(conn, ctxt); - commitConnection(conn); - return groups; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } + return getConnection(); } /** - *

- * Get the names of all of the {@link org.quartz.Job} s that - * have the given group name. - *

+ * 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(). * - *

- * If there are no jobs in the given group name, the result should be a - * zero-length array (not null). - *

- */ - public String[] getJobNames(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - String[] jobNames = getJobNames(conn, ctxt, groupName); - commitConnection(conn); - return jobNames; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the names of all of the {@link org.quartz.Trigger} s - * that have the given group name. - *

+ * @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. * - *

- * If there are no triggers in the given group name, the result should be a - * zero-length array (not null). - *

+ * @see JobStoreSupport#executeInNonManagedTXLock(String, TransactionCallback) + * @see JobStoreCMT#executeInLock(String, TransactionCallback) + * @see JobStoreSupport#getNonManagedTXConnection() + * @see JobStoreSupport#getConnection() */ - public String[] getTriggerNames(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - String[] triggerNames = getTriggerNames(conn, ctxt, groupName); - commitConnection(conn); - return triggerNames; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } + @Override + protected Object executeInLock( + String lockName, + TransactionCallback txCallback) throws JobPersistenceException { + return executeInNonManagedTXLock(lockName, txCallback, null); } - - /** - *

- * Get the names of all of the {@link org.quartz.Job} - * groups. - *

- * - *

- * If there are no known group names, the result should be a zero-length - * array (not null). - *

- */ - public String[] getJobGroupNames(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - String[] groupNames = getJobGroupNames(conn, ctxt); - commitConnection(conn); - return groupNames; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the names of all of the {@link org.quartz.Trigger} - * groups. - *

- * - *

- * If there are no known group names, the result should be a zero-length - * array (not null). - *

- */ - public String[] getTriggerGroupNames(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - String[] triggerGroups = getTriggerGroupNames(conn, ctxt); - commitConnection(conn); - return triggerGroups; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the names of all of the {@link 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). - *

- */ - public String[] getCalendarNames(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - String[] calNames = getCalendarNames(conn, ctxt); - commitConnection(conn); - return calNames; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - /** - *

- * 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) throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getTriggersForJob(conn, ctxt, jobName, groupName); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - /** - *

- * Get the current state of the identified {@link Trigger}. - *

- * - * @see Trigger#STATE_NORMAL - * @see Trigger#STATE_PAUSED - * @see Trigger#STATE_COMPLETE - * @see Trigger#STATE_ERROR - * @see Trigger#STATE_NONE - */ - public int getTriggerState(SchedulingContext ctxt, String triggerName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); - try { - // no locks necessary for read... - return getTriggerState(conn, ctxt, triggerName, groupName); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - closeConnection(conn); - } - } - - //--------------------------------------------------------------------------- - // trigger state manipulation methods - //--------------------------------------------------------------------------- - - /** - *

- * Pause the {@link org.quartz.Trigger} with the given name. - *

- * - * @see #resumeTrigger(SchedulingContext, String, String) - */ - public void pauseTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - pauseTrigger(conn, ctxt, triggerName, groupName); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Pause all of the {@link org.quartz.Trigger}s in the - * given group. - *

- * - * @see #resumeTriggerGroup(SchedulingContext, String) - */ - public void pauseTriggerGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - pauseTriggerGroup(conn, ctxt, groupName); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Pause the {@link org.quartz.Job} with the given name - by - * pausing all of its current Triggers. - *

- * - * @see #resumeJob(SchedulingContext, String, String) - */ - public void pauseJob(SchedulingContext ctxt, String jobName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - Trigger[] triggers = getTriggersForJob(conn, ctxt, jobName, - groupName); - for (int j = 0; j < triggers.length; j++) { - pauseTrigger(conn, ctxt, triggers[j].getName(), triggers[j] - .getGroup()); - } - - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Pause all of the {@link org.quartz.Job}s in the given - * group - by pausing all of their Triggers. - *

- * - * @see #resumeJobGroup(SchedulingContext, String) - */ - public void pauseJobGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - String[] jobNames = getJobNames(conn, ctxt, groupName); - - for (int i = 0; i < jobNames.length; i++) { - Trigger[] triggers = getTriggersForJob(conn, ctxt, jobNames[i], - groupName); - for (int j = 0; j < triggers.length; j++) { - pauseTrigger(conn, ctxt, triggers[j].getName(), triggers[j] - .getGroup()); - } - } - - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Resume (un-pause) the {@link 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. - *

- * - * @see #pauseTrigger(SchedulingContext, String, String) - */ - public void resumeTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - resumeTrigger(conn, ctxt, triggerName, groupName); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Resume (un-pause) all of the {@link 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. - *

- * - * @see #pauseTriggerGroup(SchedulingContext, String) - */ - public void resumeTriggerGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - resumeTriggerGroup(conn, ctxt, groupName); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Resume (un-pause) the {@link 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. - *

- * - * @see #pauseJob(SchedulingContext, String, String) - */ - public void resumeJob(SchedulingContext ctxt, String jobName, - String groupName) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - Trigger[] triggers = getTriggersForJob(conn, ctxt, jobName, - groupName); - for (int j = 0; j < triggers.length; j++) { - resumeTrigger(conn, ctxt, triggers[j].getName(), triggers[j] - .getGroup()); - } - - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Resume (un-pause) all of the {@link 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. - *

- * - * @see #pauseJobGroup(SchedulingContext, String) - */ - public void resumeJobGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - String[] jobNames = getJobNames(conn, ctxt, groupName); - - for (int i = 0; i < jobNames.length; i++) { - Trigger[] triggers = getTriggersForJob(conn, ctxt, jobNames[i], - groupName); - for (int j = 0; j < triggers.length; j++) { - resumeTrigger(conn, ctxt, triggers[j].getName(), - triggers[j].getGroup()); - } - } - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Pause all triggers - equivalent of calling pauseTriggerGroup(group) - * on every group. - *

- * - *

- * When resumeAll() is called (to un-pause), trigger misfire - * instructions WILL be applied. - *

- * - * @see #resumeAll(SchedulingContext) - * @see #pauseTriggerGroup(SchedulingContext, String) - */ - public void pauseAll(SchedulingContext ctxt) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - pauseAll(conn, ctxt); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * 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. - *

- * - * @see #pauseAll(SchedulingContext) - */ - public void resumeAll(SchedulingContext ctxt) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - resumeAll(conn, ctxt); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - //--------------------------------------------------------------------------- - // trigger firing methods - //--------------------------------------------------------------------------- - - /** - *

- * 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) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - Trigger trigger = acquireNextTrigger(conn, ctxt, noLaterThan); - commitConnection(conn); - return trigger; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Inform the JobStore that the scheduler no longer plans to - * fire the given Trigger, that it had previously acquired - * (reserved). - *

- */ - public void releaseAcquiredTrigger(SchedulingContext ctxt, Trigger trigger) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - releaseAcquiredTrigger(conn, ctxt, trigger); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Inform the JobStore that the scheduler is now firing the - * given Trigger (executing its associated Job), - * that it had previously acquired (reserved). - *

- * - * @return null if the trigger or it's job or calendar no longer exist, or - * if the trigger was not successfully put into the 'executing' - * state. - */ - public TriggerFiredBundle triggerFired(SchedulingContext ctxt, - Trigger trigger) throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - TriggerFiredBundle tfb = null; - JobPersistenceException err = null; - try { - tfb = triggerFired(conn, ctxt, trigger); - } catch (JobPersistenceException jpe) { - if (jpe.getErrorCode() != SchedulerException.ERR_PERSISTENCE_JOB_DOES_NOT_EXIST) - throw jpe; - err = jpe; - } - - commitConnection(conn); - if (err != null) throw err; - return tfb; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - /** - *

- * Inform the JobStore that the scheduler has completed the - * firing of the given Trigger (and the execution its - * associated Job), and that the {@link org.quartz.JobDataMap} - * in the given JobDetail should be updated if the Job - * is stateful. - *

- */ - public void triggeredJobComplete(SchedulingContext ctxt, Trigger trigger, - JobDetail jobDetail, int triggerInstCode) - throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - triggeredJobComplete(conn, ctxt, trigger, jobDetail, - triggerInstCode); - commitConnection(conn); - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - protected boolean doRecoverMisfires() throws JobPersistenceException { - Connection conn = getConnection(); - boolean transOwner = false; - boolean moreToDo = false; - try { - getLockHandler().obtainLock(conn, LOCK_TRIGGER_ACCESS); - transOwner = true; - //getLockHandler().obtainLock(conn, LOCK_JOB_ACCESS); - - try { - moreToDo = recoverMisfiredJobs(conn, false); - } catch (Exception e) { - throw new JobPersistenceException(e.getMessage(), e); - } - - commitConnection(conn); - - return moreToDo; - } catch (JobPersistenceException e) { - rollbackConnection(conn); - throw e; - } finally { - try { - releaseLock(conn, LOCK_TRIGGER_ACCESS, transOwner); - } finally { - closeConnection(conn); - } - } - } - - protected boolean doCheckin() throws JobPersistenceException { - Connection conn = getConnection(); - - boolean transOwner = false; - boolean transStateOwner = false; - boolean recovered = false; - - 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) - List failedRecords = (firstCheckIn) ? null : clusterCheckIn(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 { - closeConnection(conn); - } - } - } - - firstCheckIn = false; - - return recovered; - } } - // EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/LockException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/LockException.java (.../LockException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/LockException.java (.../LockException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import org.quartz.JobPersistenceException; @@ -34,6 +31,8 @@ */ public class LockException extends JobPersistenceException { + private static final long serialVersionUID = 3993800462589137228L; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -46,7 +45,7 @@ super(msg); } - public LockException(String msg, Exception cause) { + public LockException(String msg, Throwable cause) { super(msg, cause); } } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/MSSQLDelegate.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/MSSQLDelegate.java (.../MSSQLDelegate.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/MSSQLDelegate.java (.../MSSQLDelegate.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.io.IOException; @@ -26,7 +23,8 @@ import java.sql.ResultSet; import java.sql.SQLException; -import org.apache.commons.logging.Log; +import org.quartz.spi.ClassLoadHelper; +import org.slf4j.Logger; /** *

@@ -36,24 +34,7 @@ * @author Jeffrey Wescott */ public class MSSQLDelegate extends StdJDBCDelegate { - /** - *

- * Create new MSSQLDelegate instance. - *

- * - * @param logger - * 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 //--------------------------------------------------------------------------- @@ -75,22 +56,30 @@ * @throws IOException * if deserialization causes an error */ + @Override protected Object getObjectFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + throws ClassNotFoundException, IOException, SQLException { InputStream binaryInput = rs.getBinaryStream(colName); - if(binaryInput == null) + if(binaryInput == null || binaryInput.available() == 0) { return null; - + } + + Object obj = null; + ObjectInputStream in = new ObjectInputStream(binaryInput); - Object obj = in.readObject(); - in.close(); + try { + obj = in.readObject(); + } finally { + in.close(); + } return obj; } - protected Object getJobDetailFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + @Override + protected Object getJobDataFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { if (canUseProperties()) { InputStream binaryInput = rs.getBinaryStream(colName); return binaryInput; Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/NoSuchDelegateException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/NoSuchDelegateException.java (.../NoSuchDelegateException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/NoSuchDelegateException.java (.../NoSuchDelegateException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import org.quartz.JobPersistenceException; @@ -31,6 +28,9 @@ * @author Jeffrey Wescott */ public class NoSuchDelegateException extends JobPersistenceException { + + private static final long serialVersionUID = -4255865028975822979L; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -42,6 +42,10 @@ public NoSuchDelegateException(String msg) { super(msg); } + + public NoSuchDelegateException(String msg, Throwable cause) { + super(msg, cause); + } } // EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/PointbaseDelegate.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/PointbaseDelegate.java (.../PointbaseDelegate.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/PointbaseDelegate.java (.../PointbaseDelegate.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.io.ByteArrayInputStream; @@ -31,12 +28,11 @@ import java.sql.ResultSet; import java.sql.SQLException; -import org.apache.commons.logging.Log; import org.quartz.Calendar; -import org.quartz.CronTrigger; import org.quartz.JobDetail; -import org.quartz.SimpleTrigger; -import org.quartz.Trigger; +import org.quartz.spi.ClassLoadHelper; +import org.quartz.spi.OperableTrigger; +import org.slf4j.Logger; /** *

@@ -47,37 +43,6 @@ */ 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 //--------------------------------------------------------------------------- @@ -95,8 +60,9 @@ * @throws IOException * if there were problems serializing the JobDataMap */ + @Override public int insertJobDetail(Connection conn, JobDetail job) - throws IOException, SQLException { + throws IOException, SQLException { //log.debug( "Inserting JobDetail " + job ); ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); int len = baos.toByteArray().length; @@ -108,32 +74,21 @@ try { ps = conn.prepareStatement(rtp(INSERT_JOB_DETAIL)); - ps.setString(1, job.getName()); - ps.setString(2, job.getGroup()); + ps.setString(1, job.getKey().getName()); + ps.setString(2, job.getKey().getGroup()); ps.setString(3, job.getDescription()); ps.setString(4, job.getJobClass().getName()); - ps.setBoolean(5, job.isDurable()); - ps.setBoolean(6, job.isVolatile()); - ps.setBoolean(7, job.isStateful()); - ps.setBoolean(8, job.requestsRecovery()); + setBoolean(ps, 5, job.isDurable()); + setBoolean(ps, 6, job.isConcurrentExectionDisallowed()); + setBoolean(ps, 7, job.isPersistJobDataAfterExecution()); + setBoolean(ps, 8, job.requestsRecovery()); ps.setBinaryStream(9, bais, len); insertResult = ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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; } @@ -150,8 +105,9 @@ * @throws IOException * if there were problems serializing the JobDataMap */ + @Override public int updateJobDetail(Connection conn, JobDetail job) - throws IOException, SQLException { + throws IOException, SQLException { //log.debug( "Updating job detail " + job ); ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); int len = baos.toByteArray().length; @@ -165,36 +121,24 @@ ps = conn.prepareStatement(rtp(UPDATE_JOB_DETAIL)); ps.setString(1, job.getDescription()); ps.setString(2, job.getJobClass().getName()); - ps.setBoolean(3, job.isDurable()); - ps.setBoolean(4, job.isVolatile()); - ps.setBoolean(5, job.isStateful()); - ps.setBoolean(6, job.requestsRecovery()); + setBoolean(ps, 3, job.isDurable()); + setBoolean(ps, 4, job.isConcurrentExectionDisallowed()); + setBoolean(ps, 5, job.isPersistJobDataAfterExecution()); + setBoolean(ps, 6, job.requestsRecovery()); ps.setBinaryStream(7, bais, len); - ps.setString(8, job.getName()); - ps.setString(9, job.getGroup()); + ps.setString(8, job.getKey().getName()); + ps.setString(9, job.getKey().getGroup()); insertResult = ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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, + @Override + public int insertTrigger(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { ByteArrayOutputStream baos = serializeJobData(trigger.getJobDataMap()); @@ -207,58 +151,55 @@ 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()); - ps.setBoolean(5, trigger.isVolatile()); - ps.setString(6, trigger.getDescription()); - ps.setBigDecimal(7, new BigDecimal(String.valueOf(trigger + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + ps.setString(3, trigger.getJobKey().getName()); + ps.setString(4, trigger.getJobKey().getGroup()); + ps.setString(5, trigger.getDescription()); + ps.setBigDecimal(6, 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) { - ps.setString(10, TTYPE_SIMPLE); - } else if (trigger instanceof CronTrigger) { - ps.setString(10, TTYPE_CRON); - } else { // (trigger instanceof BlobTrigger) - ps.setString(10, TTYPE_BLOB); - } - ps.setBigDecimal(11, new BigDecimal(String.valueOf(trigger + ps.setBigDecimal(7, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(8, state); + + TriggerPersistenceDelegate tDel = findTriggerPersistenceDelegate(trigger); + + String type = TTYPE_BLOB; + if(tDel != null) + type = tDel.getHandledTriggerTypeDiscriminator(); + ps.setString(9, type); + + ps.setBigDecimal(10, 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.setBigDecimal(11, new BigDecimal(String.valueOf(endTime))); + ps.setString(12, trigger.getCalendarName()); + ps.setInt(13, trigger.getMisfireInstruction()); + ps.setBinaryStream(14, bais, len); + ps.setInt(15, trigger.getPriority()); insertResult = ps.executeUpdate(); + + if(tDel == null) + insertBlobTrigger(conn, trigger); + else + tDel.insertExtendedTriggerProperties(conn, trigger, state, jobDetail); + } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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, + @Override + public int updateTrigger(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { ByteArrayOutputStream baos = serializeJobData(trigger.getJobDataMap()); @@ -273,62 +214,55 @@ try { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER)); - ps.setString(1, trigger.getJobName()); - ps.setString(2, trigger.getJobGroup()); - ps.setBoolean(3, trigger.isVolatile()); - ps.setString(4, trigger.getDescription()); + ps.setString(1, trigger.getJobKey().getName()); + ps.setString(2, trigger.getJobKey().getGroup()); + ps.setString(3, trigger.getDescription()); long nextFireTime = -1; if (trigger.getNextFireTime() != null) { nextFireTime = trigger.getNextFireTime().getTime(); } - ps.setBigDecimal(5, new BigDecimal(String.valueOf(nextFireTime))); + ps.setBigDecimal(4, 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) { - // updateSimpleTrigger(conn, (SimpleTrigger)trigger); - ps.setString(8, TTYPE_SIMPLE); - } else if (trigger instanceof CronTrigger) { - // 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 + ps.setBigDecimal(5, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(6, state); + + TriggerPersistenceDelegate tDel = findTriggerPersistenceDelegate(trigger); + + String type = TTYPE_BLOB; + if(tDel != null) + type = tDel.getHandledTriggerTypeDiscriminator(); + + ps.setString(7, type); + + ps.setBigDecimal(8, 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.setBigDecimal(9, new BigDecimal(String.valueOf(endTime))); + ps.setString(10, trigger.getCalendarName()); + ps.setInt(11, trigger.getMisfireInstruction()); + + ps.setInt(12, trigger.getPriority()); ps.setBinaryStream(13, bais, len); - ps.setString(14, trigger.getName()); - ps.setString(15, trigger.getGroup()); + ps.setString(14, trigger.getKey().getName()); + ps.setString(15, trigger.getKey().getGroup()); insertResult = ps.executeUpdate(); + + if(tDel == null) + updateBlobTrigger(conn, trigger); + else + tDel.updateExtendedTriggerProperties(conn, trigger, state, jobDetail); + } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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; } @@ -343,8 +277,9 @@ * the job to update * @return the number of rows updated */ + @Override public int updateJobData(Connection conn, JobDetail job) - throws IOException, SQLException { + throws IOException, SQLException { //log.debug( "Updating Job Data for Job " + job ); ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); int len = baos.toByteArray().length; @@ -354,17 +289,12 @@ try { ps = conn.prepareStatement(rtp(UPDATE_JOB_DATA)); ps.setBinaryStream(1, bais, len); - ps.setString(2, job.getName()); - ps.setString(3, job.getGroup()); + ps.setString(2, job.getKey().getName()); + ps.setString(3, job.getKey().getGroup()); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -391,6 +321,7 @@ * @throws IOException * if there were problems serializing the calendar */ + @Override public int insertCalendar(Connection conn, String calendarName, Calendar calendar) throws IOException, SQLException { //log.debug( "Inserting Calendar " + calendarName + " : " + calendar @@ -408,12 +339,7 @@ return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -432,6 +358,7 @@ * @throws IOException * if there were problems serializing the calendar */ + @Override public int updateCalendar(Connection conn, String calendarName, Calendar calendar) throws IOException, SQLException { //log.debug( "Updating calendar " + calendarName + " : " + calendar ); @@ -448,12 +375,7 @@ return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -478,19 +400,23 @@ * @throws IOException * if deserialization causes an error */ + @Override protected Object getObjectFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + throws ClassNotFoundException, IOException, SQLException { //log.debug( "Getting blob from column: " + colName ); Object obj = null; byte binaryData[] = rs.getBytes(colName); InputStream binaryInput = new ByteArrayInputStream(binaryData); - if (null != binaryInput) { + if (null != binaryInput && binaryInput.available() != 0) { ObjectInputStream in = new ObjectInputStream(binaryInput); - obj = in.readObject(); - in.close(); + try { + obj = in.readObject(); + } finally { + in.close(); + } } return obj; @@ -513,13 +439,15 @@ * @throws IOException * if deserialization causes an error */ - protected Object getJobDetailFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + @Override + protected Object getJobDataFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { //log.debug( "Getting Job details from blob in col " + colName ); if (canUseProperties()) { byte data[] = rs.getBytes(colName); - if(data == null) + if(data == null) { return null; + } InputStream binaryInput = new ByteArrayInputStream(data); return binaryInput; } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/PostgreSQLDelegate.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/PostgreSQLDelegate.java (.../PostgreSQLDelegate.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/PostgreSQLDelegate.java (.../PostgreSQLDelegate.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.io.ByteArrayInputStream; @@ -27,7 +24,8 @@ import java.sql.ResultSet; import java.sql.SQLException; -import org.apache.commons.logging.Log; +import org.quartz.spi.ClassLoadHelper; +import org.slf4j.Logger; /** *

@@ -37,37 +35,7 @@ * @author Jeffrey Wescott */ public class PostgreSQLDelegate extends StdJDBCDelegate { - /** - *

- * Create new PostgreSQLDelegate instance. - *

- * - * @param logger - * 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 logger - * 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 //--------------------------------------------------------------------------- @@ -89,31 +57,38 @@ * @throws IOException * if deserialization causes an error */ + @Override protected Object getObjectFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + throws ClassNotFoundException, IOException, SQLException { InputStream binaryInput = null; byte[] bytes = rs.getBytes(colName); Object obj = null; - if(bytes != null) { + if(bytes != null && bytes.length != 0) { binaryInput = new ByteArrayInputStream(bytes); ObjectInputStream in = new ObjectInputStream(binaryInput); - obj = in.readObject(); - in.close(); + try { + obj = in.readObject(); + } finally { + in.close(); + } + } return obj; } - protected Object getJobDetailFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + @Override + protected Object getJobDataFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { if (canUseProperties()) { InputStream binaryInput = null; byte[] bytes = rs.getBytes(colName); - if(bytes == null || bytes.length == 0) + if(bytes == null || bytes.length == 0) { return null; + } binaryInput = new ByteArrayInputStream(bytes); return binaryInput; } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SchedulerStateRecord.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SchedulerStateRecord.java (.../SchedulerStateRecord.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SchedulerStateRecord.java (.../SchedulerStateRecord.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; /** @@ -29,6 +26,8 @@ */ public class SchedulerStateRecord implements java.io.Serializable { + private static final long serialVersionUID = -715704959016191445L; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -43,8 +42,6 @@ private long checkinInterval; - private String recoverer; - /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -67,12 +64,6 @@ /** */ - public String getRecoverer() { - return recoverer; - } - - /** - */ public String getSchedulerInstanceId() { return schedulerInstanceId; } @@ -91,12 +82,6 @@ /** */ - public void setRecoverer(String string) { - recoverer = string; - } - - /** - */ public void setSchedulerInstanceId(String string) { schedulerInstanceId = string; } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/Semaphore.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/Semaphore.java (.../Semaphore.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/Semaphore.java (.../Semaphore.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.sql.Connection; @@ -42,6 +39,9 @@ * 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; @@ -50,12 +50,14 @@ * Release the lock on the identified resource if it is held by the calling * thread. */ - void releaseLock(Connection conn, String lockName) throws LockException; + void releaseLock(String lockName) throws LockException; /** - * Determine whether the calling thread owns a lock on the identified - * resource. + * Whether this Semaphore implementation requires a database connection for + * its lock management operations. + * + * @see #obtainLock(Connection, String) + * @see #releaseLock(String) */ - boolean isLockOwner(Connection conn, String lockName) throws LockException; - + boolean requiresConnection(); } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimplePropertiesTriggerPersistenceDelegateSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimplePropertiesTriggerPersistenceDelegateSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimplePropertiesTriggerPersistenceDelegateSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,192 @@ +package org.quartz.impl.jdbcjobstore; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.quartz.JobDetail; +import org.quartz.ScheduleBuilder; +import org.quartz.TriggerKey; +import org.quartz.spi.OperableTrigger; + +/** + * A base implementation of {@link TriggerPersistenceDelegate} that persists + * trigger fields in the "QRTZ_SIMPROP_TRIGGERS" table. This allows extending + * concrete classes to simply implement a couple methods that do the work of + * getting/setting the trigger's fields, and creating the {@link ScheduleBuilder} + * for the particular type of trigger. + * + * @see CalendarIntervalTriggerPersistenceDelegate for an example extension + * + * @author jhouse + */ +public abstract class SimplePropertiesTriggerPersistenceDelegateSupport implements TriggerPersistenceDelegate, StdJDBCConstants { + + protected static final String TABLE_SIMPLE_PROPERTIES_TRIGGERS = "SIMPROP_TRIGGERS"; + + protected static final String COL_STR_PROP_1 = "STR_PROP_1"; + protected static final String COL_STR_PROP_2 = "STR_PROP_2"; + protected static final String COL_STR_PROP_3 = "STR_PROP_3"; + protected static final String COL_INT_PROP_1 = "INT_PROP_1"; + protected static final String COL_INT_PROP_2 = "INT_PROP_2"; + protected static final String COL_LONG_PROP_1 = "LONG_PROP_1"; + protected static final String COL_LONG_PROP_2 = "LONG_PROP_2"; + protected static final String COL_DEC_PROP_1 = "DEC_PROP_1"; + protected static final String COL_DEC_PROP_2 = "DEC_PROP_2"; + protected static final String COL_BOOL_PROP_1 = "BOOL_PROP_1"; + protected static final String COL_BOOL_PROP_2 = "BOOL_PROP_2"; + + protected static final String SELECT_SIMPLE_PROPS_TRIGGER = "SELECT *" + " FROM " + + TABLE_PREFIX_SUBST + TABLE_SIMPLE_PROPERTIES_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + protected static final String DELETE_SIMPLE_PROPS_TRIGGER = "DELETE FROM " + + TABLE_PREFIX_SUBST + TABLE_SIMPLE_PROPERTIES_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + protected static final String INSERT_SIMPLE_PROPS_TRIGGER = "INSERT INTO " + + TABLE_PREFIX_SUBST + TABLE_SIMPLE_PROPERTIES_TRIGGERS + " (" + + COL_SCHEDULER_NAME + ", " + + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", " + + COL_STR_PROP_1 + ", " + COL_STR_PROP_2 + ", " + COL_STR_PROP_3 + ", " + + COL_INT_PROP_1 + ", " + COL_INT_PROP_2 + ", " + + COL_LONG_PROP_1 + ", " + COL_LONG_PROP_2 + ", " + + COL_DEC_PROP_1 + ", " + COL_DEC_PROP_2 + ", " + + COL_BOOL_PROP_1 + ", " + COL_BOOL_PROP_2 + + ") " + " VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + protected static final String UPDATE_SIMPLE_PROPS_TRIGGER = "UPDATE " + + TABLE_PREFIX_SUBST + TABLE_SIMPLE_PROPERTIES_TRIGGERS + " SET " + + COL_STR_PROP_1 + " = ?, " + COL_STR_PROP_2 + " = ?, " + COL_STR_PROP_3 + " = ?, " + + COL_INT_PROP_1 + " = ?, " + COL_INT_PROP_2 + " = ?, " + + COL_LONG_PROP_1 + " = ?, " + COL_LONG_PROP_2 + " = ?, " + + COL_DEC_PROP_1 + " = ?, " + COL_DEC_PROP_2 + " = ?, " + + COL_BOOL_PROP_1 + " = ?, " + COL_BOOL_PROP_2 + + " = ? WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + protected String tablePrefix; + + protected String schedNameLiteral; + + public void initialize(String theTablePrefix, String schedName) { + this.tablePrefix = theTablePrefix; + this.schedNameLiteral = "'" + schedName + "'"; + } + + protected abstract SimplePropertiesTriggerProperties getTriggerProperties(OperableTrigger trigger); + + protected abstract TriggerPropertyBundle getTriggerPropertyBundle(SimplePropertiesTriggerProperties properties); + + public int deleteExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(Util.rtp(DELETE_SIMPLE_PROPS_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); + + return ps.executeUpdate(); + } finally { + Util.closeStatement(ps); + } + } + + public int insertExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { + + SimplePropertiesTriggerProperties properties = getTriggerProperties(trigger); + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(Util.rtp(INSERT_SIMPLE_PROPS_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + ps.setString(3, properties.getString1()); + ps.setString(4, properties.getString2()); + ps.setString(5, properties.getString3()); + ps.setInt(6, properties.getInt1()); + ps.setInt(7, properties.getInt2()); + ps.setLong(8, properties.getLong1()); + ps.setLong(9, properties.getLong2()); + ps.setBigDecimal(10, properties.getDecimal1()); + ps.setBigDecimal(11, properties.getDecimal2()); + ps.setBoolean(12, properties.isBoolean1()); + ps.setBoolean(13, properties.isBoolean2()); + + return ps.executeUpdate(); + } finally { + Util.closeStatement(ps); + } + } + + public TriggerPropertyBundle loadExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException { + + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(Util.rtp(SELECT_SIMPLE_PROPS_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); + rs = ps.executeQuery(); + + if (rs.next()) { + SimplePropertiesTriggerProperties properties = new SimplePropertiesTriggerProperties(); + + properties.setString1(rs.getString(COL_STR_PROP_1)); + properties.setString2(rs.getString(COL_STR_PROP_2)); + properties.setString3(rs.getString(COL_STR_PROP_3)); + properties.setInt1(rs.getInt(COL_INT_PROP_1)); + properties.setInt2(rs.getInt(COL_INT_PROP_2)); + properties.setLong1(rs.getInt(COL_LONG_PROP_1)); + properties.setLong2(rs.getInt(COL_LONG_PROP_2)); + properties.setDecimal1(rs.getBigDecimal(COL_DEC_PROP_1)); + properties.setDecimal2(rs.getBigDecimal(COL_DEC_PROP_2)); + properties.setBoolean1(rs.getBoolean(COL_BOOL_PROP_1)); + properties.setBoolean2(rs.getBoolean(COL_BOOL_PROP_2)); + + return getTriggerPropertyBundle(properties); + } + + throw new IllegalStateException("No record found for selection of Trigger with key: '" + triggerKey + "' and statement: " + Util.rtp(SELECT_SIMPLE_TRIGGER, tablePrefix, schedNameLiteral)); + } finally { + Util.closeResultSet(rs); + Util.closeStatement(ps); + } + } + + public int updateExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { + + SimplePropertiesTriggerProperties properties = getTriggerProperties(trigger); + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(Util.rtp(UPDATE_SIMPLE_PROPS_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, properties.getString1()); + ps.setString(2, properties.getString2()); + ps.setString(3, properties.getString3()); + ps.setInt(4, properties.getInt1()); + ps.setInt(5, properties.getInt2()); + ps.setLong(6, properties.getLong1()); + ps.setLong(7, properties.getLong2()); + ps.setBigDecimal(8, properties.getDecimal1()); + ps.setBigDecimal(9, properties.getDecimal2()); + ps.setBoolean(10, properties.isBoolean1()); + ps.setBoolean(11, properties.isBoolean2()); + ps.setString(12, trigger.getKey().getName()); + ps.setString(13, trigger.getKey().getGroup()); + + return ps.executeUpdate(); + } finally { + Util.closeStatement(ps); + } + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimplePropertiesTriggerProperties.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimplePropertiesTriggerProperties.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimplePropertiesTriggerProperties.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,92 @@ +package org.quartz.impl.jdbcjobstore; + +import java.math.BigDecimal; + +public class SimplePropertiesTriggerProperties { + + private String string1; + private String string2; + private String string3; + + private int int1; + private int int2; + + private long long1; + private long long2; + + private BigDecimal decimal1; + private BigDecimal decimal2; + + private boolean boolean1; + private boolean boolean2; + + + public String getString1() { + return string1; + } + public void setString1(String string1) { + this.string1 = string1; + } + public String getString2() { + return string2; + } + public void setString2(String string2) { + this.string2 = string2; + } + public String getString3() { + return string3; + } + public void setString3(String string3) { + this.string3 = string3; + } + public int getInt1() { + return int1; + } + public void setInt1(int int1) { + this.int1 = int1; + } + public int getInt2() { + return int2; + } + public void setInt2(int int2) { + this.int2 = int2; + } + public long getLong1() { + return long1; + } + public void setLong1(long long1) { + this.long1 = long1; + } + public long getLong2() { + return long2; + } + public void setLong2(long long2) { + this.long2 = long2; + } + public BigDecimal getDecimal1() { + return decimal1; + } + public void setDecimal1(BigDecimal decimal1) { + this.decimal1 = decimal1; + } + public BigDecimal getDecimal2() { + return decimal2; + } + public void setDecimal2(BigDecimal decimal2) { + this.decimal2 = decimal2; + } + public boolean isBoolean1() { + return boolean1; + } + public void setBoolean1(boolean boolean1) { + this.boolean1 = boolean1; + } + public boolean isBoolean2() { + return boolean2; + } + public void setBoolean2(boolean boolean2) { + this.boolean2 = boolean2; + } + + +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimpleSemaphore.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimpleSemaphore.java (.../SimpleSemaphore.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimpleSemaphore.java (.../SimpleSemaphore.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,21 +15,18 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.sql.Connection; import java.util.HashSet; -import java.util.List; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * An interface for providing thread/resource locking in order to protect - * resources from being altered by multiple threads at the same time. + * 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 */ @@ -43,10 +40,12 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - ThreadLocal lockOwners = new ThreadLocal(); + ThreadLocal> lockOwners = new ThreadLocal>(); - HashSet locks = new HashSet(); + HashSet locks = new HashSet(); + private final Logger log = LoggerFactory.getLogger(getClass()); + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -55,15 +54,14 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - Log getLog() { - return LogFactory.getLog(getClass()); - //return LogFactory.getLog("LOCK:"+Thread.currentThread().getName()); + protected Logger getLog() { + return log; } - private HashSet getThreadLocks() { - HashSet threadLocks = (HashSet) lockOwners.get(); + private HashSet getThreadLocks() { + HashSet threadLocks = lockOwners.get(); if (threadLocks == null) { - threadLocks = new HashSet(); + threadLocks = new HashSet(); lockOwners.set(threadLocks); } return threadLocks; @@ -79,42 +77,44 @@ lockName = lockName.intern(); - Log log = getLog(); - - if(log.isDebugEnabled()) + if(log.isDebugEnabled()) { log.debug( "Lock '" + lockName + "' is desired by: " + Thread.currentThread().getName()); + } - if (!isLockOwner(conn, lockName)) { - if(log.isDebugEnabled()) + if (!isLockOwner(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()) + if(log.isDebugEnabled()) { log.debug( "Lock '" + lockName + "' was not obtained by: " + Thread.currentThread().getName()); + } } } - if(log.isDebugEnabled()) + 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")); + } 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; } @@ -123,40 +123,43 @@ * Release the lock on the identified resource if it is held by the calling * thread. */ - public synchronized void releaseLock(Connection conn, String lockName) { + public synchronized void releaseLock(String lockName) { lockName = lockName.intern(); - if (isLockOwner(conn, lockName)) { - if(getLog().isDebugEnabled()) + if (isLockOwner(lockName)) { + if(getLog().isDebugEnabled()) { getLog().debug( "Lock '" + lockName + "' retuned by: " + Thread.currentThread().getName()); + } getThreadLocks().remove(lockName); locks.remove(lockName); - this.notify(); - } 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")); + 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) { + public synchronized boolean isLockOwner(String lockName) { lockName = lockName.intern(); return getThreadLocks().contains(lockName); } - public void init(Connection conn, List listOfLocks) { - // nothing to do... + /** + * This Semaphore implementation does not use the database. + */ + public boolean requiresConnection() { + return false; } - } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimpleTriggerPersistenceDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimpleTriggerPersistenceDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SimpleTriggerPersistenceDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,123 @@ +package org.quartz.impl.jdbcjobstore; + +import java.io.IOException; +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.quartz.JobDetail; +import org.quartz.SimpleScheduleBuilder; +import org.quartz.SimpleTrigger; +import org.quartz.TriggerKey; +import org.quartz.impl.triggers.SimpleTriggerImpl; +import org.quartz.spi.OperableTrigger; + +public class SimpleTriggerPersistenceDelegate implements TriggerPersistenceDelegate, StdJDBCConstants { + + protected String tablePrefix; + protected String schedNameLiteral; + + public void initialize(String theTablePrefix, String schedName) { + this.tablePrefix = theTablePrefix; + this.schedNameLiteral = "'" + schedName + "'"; + } + + public String getHandledTriggerTypeDiscriminator() { + return TTYPE_SIMPLE; + } + + public boolean canHandleTriggerType(OperableTrigger trigger) { + return ((trigger instanceof SimpleTriggerImpl) && !((SimpleTriggerImpl)trigger).hasAdditionalProperties()); + } + + public int deleteExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(Util.rtp(DELETE_SIMPLE_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); + + return ps.executeUpdate(); + } finally { + Util.closeStatement(ps); + } + } + + public int insertExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { + + SimpleTrigger simpleTrigger = (SimpleTrigger)trigger; + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(Util.rtp(INSERT_SIMPLE_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + ps.setInt(3, simpleTrigger.getRepeatCount()); + ps.setBigDecimal(4, new BigDecimal(String.valueOf(simpleTrigger.getRepeatInterval()))); + ps.setInt(5, simpleTrigger.getTimesTriggered()); + + return ps.executeUpdate(); + } finally { + Util.closeStatement(ps); + } + } + + public TriggerPropertyBundle loadExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException { + + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(Util.rtp(SELECT_SIMPLE_TRIGGER, tablePrefix, schedNameLiteral)); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); + 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); + + SimpleScheduleBuilder sb = SimpleScheduleBuilder.simpleSchedule() + .withRepeatCount(repeatCount) + .withIntervalInMilliseconds(repeatInterval); + + String[] statePropertyNames = { "timesTriggered" }; + Object[] statePropertyValues = { timesTriggered }; + + return new TriggerPropertyBundle(sb, statePropertyNames, statePropertyValues); + } + + throw new IllegalStateException("No record found for selection of Trigger with key: '" + triggerKey + "' and statement: " + Util.rtp(SELECT_SIMPLE_TRIGGER, tablePrefix, schedNameLiteral)); + } finally { + Util.closeResultSet(rs); + Util.closeStatement(ps); + } + } + + public int updateExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { + + SimpleTrigger simpleTrigger = (SimpleTrigger)trigger; + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(Util.rtp(UPDATE_SIMPLE_TRIGGER, tablePrefix, schedNameLiteral)); + + ps.setInt(1, simpleTrigger.getRepeatCount()); + ps.setBigDecimal(2, new BigDecimal(String.valueOf(simpleTrigger.getRepeatInterval()))); + ps.setInt(3, simpleTrigger.getTimesTriggered()); + ps.setString(4, simpleTrigger.getKey().getName()); + ps.setString(5, simpleTrigger.getKey().getGroup()); + + return ps.executeUpdate(); + } finally { + Util.closeStatement(ps); + } + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/StdJDBCConstants.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/StdJDBCConstants.java (.../StdJDBCConstants.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/StdJDBCConstants.java (.../StdJDBCConstants.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,12 +15,10 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ - package org.quartz.impl.jdbcjobstore; +import org.quartz.Trigger; + /** *

* This interface extends {@link @@ -43,539 +41,630 @@ */ // table prefix substitution string - public static final String TABLE_PREFIX_SUBST = "{0}"; + String TABLE_PREFIX_SUBST = "{0}"; + // table prefix substitution string + String SCHED_NAME_SUBST = "{1}"; + // QUERIES - public static final String UPDATE_TRIGGER_STATES_FROM_OTHER_STATES = "UPDATE " + String UPDATE_TRIGGER_STATES_FROM_OTHER_STATES = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_TRIGGER_STATE + " = ?" + " WHERE " + + COL_SCHEDULER_NAME + + " = " + SCHED_NAME_SUBST + " AND (" + COL_TRIGGER_STATE + " = ? OR " - + COL_TRIGGER_STATE + " = ?"; + + COL_TRIGGER_STATE + " = ?)"; - public static final String UPDATE_TRIGGER_STATE_FROM_OTHER_STATES_BEFORE_TIME = "UPDATE " - + TABLE_PREFIX_SUBST - + TABLE_TRIGGERS - + " SET " - + COL_TRIGGER_STATE - + " = ?" - + " WHERE (" - + COL_TRIGGER_STATE - + " = ? OR " - + COL_TRIGGER_STATE + " = ?) AND " + COL_NEXT_FIRE_TIME + " < ?"; - - public static final String SELECT_MISFIRED_TRIGGERS = "SELECT * FROM " - + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " - + COL_NEXT_FIRE_TIME + " < ? ORDER BY START_TIME ASC"; - - public static final String SELECT_TRIGGERS_IN_STATE = "SELECT " + String SELECT_MISFIRED_TRIGGERS = "SELECT * FROM " + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND NOT (" + + COL_MISFIRE_INSTRUCTION + " = " + Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY + ") AND " + + COL_NEXT_FIRE_TIME + " < ? " + + "ORDER BY " + COL_NEXT_FIRE_TIME + " ASC, " + COL_PRIORITY + " DESC"; + + String SELECT_TRIGGERS_IN_STATE = "SELECT " + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + " AND " + COL_TRIGGER_STATE + " = ?"; - public static final String SELECT_MISFIRED_TRIGGERS_IN_STATE = "SELECT " - + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " - + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " - + COL_NEXT_FIRE_TIME + " < ? AND " + COL_TRIGGER_STATE + " = ?"; + String SELECT_MISFIRED_TRIGGERS_IN_STATE = "SELECT " + + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + " AND NOT (" + + COL_MISFIRE_INSTRUCTION + " = " + Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY + ") AND " + + COL_NEXT_FIRE_TIME + " < ? AND " + COL_TRIGGER_STATE + " = ? " + + "ORDER BY " + COL_NEXT_FIRE_TIME + " ASC, " + COL_PRIORITY + " DESC"; - public static final String SELECT_MISFIRED_TRIGGERS_IN_GROUP_IN_STATE = "SELECT " - + COL_TRIGGER_NAME - + " FROM " - + TABLE_PREFIX_SUBST - + TABLE_TRIGGERS - + " WHERE " - + COL_NEXT_FIRE_TIME - + " < ? AND " - + COL_TRIGGER_GROUP - + " = ? AND " + COL_TRIGGER_STATE + " = ?"; + String COUNT_MISFIRED_TRIGGERS_IN_STATE = "SELECT COUNT(" + + COL_TRIGGER_NAME + ") FROM " + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + " AND NOT (" + + COL_MISFIRE_INSTRUCTION + " = " + Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY + ") AND " + + COL_NEXT_FIRE_TIME + " < ? " + + "AND " + COL_TRIGGER_STATE + " = ?"; + + String SELECT_HAS_MISFIRED_TRIGGERS_IN_STATE = "SELECT " + + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + " AND NOT (" + + COL_MISFIRE_INSTRUCTION + " = " + Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY + ") AND " + + COL_NEXT_FIRE_TIME + " < ? " + + "AND " + COL_TRIGGER_STATE + " = ? " + + "ORDER BY " + COL_NEXT_FIRE_TIME + " ASC, " + COL_PRIORITY + " DESC"; - public static final String SELECT_VOLATILE_TRIGGERS = "SELECT " - + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " - + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + COL_IS_VOLATILE - + " = ?"; + String SELECT_MISFIRED_TRIGGERS_IN_GROUP_IN_STATE = "SELECT " + + COL_TRIGGER_NAME + + " FROM " + + TABLE_PREFIX_SUBST + + TABLE_TRIGGERS + + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + " AND NOT (" + + COL_MISFIRE_INSTRUCTION + " = " + Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY + ") AND " + + COL_NEXT_FIRE_TIME + + " < ? AND " + + COL_TRIGGER_GROUP + + " = ? AND " + COL_TRIGGER_STATE + " = ? " + + "ORDER BY " + COL_NEXT_FIRE_TIME + " ASC, " + COL_PRIORITY + " DESC"; - public static final String DELETE_FIRED_TRIGGERS = "DELETE FROM " - + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS; - public static final String INSERT_JOB_DETAIL = "INSERT INTO " - + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " (" + COL_JOB_NAME + String DELETE_FIRED_TRIGGERS = "DELETE FROM " + + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + + String INSERT_JOB_DETAIL = "INSERT INTO " + + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " (" + + COL_SCHEDULER_NAME + ", " + COL_JOB_NAME + ", " + COL_JOB_GROUP + ", " + COL_DESCRIPTION + ", " - + COL_JOB_CLASS + ", " + COL_IS_DURABLE + ", " + COL_IS_VOLATILE - + ", " + COL_IS_STATEFUL + ", " + COL_REQUESTS_RECOVERY + ", " - + COL_JOB_DATAMAP + ") " + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + COL_JOB_CLASS + ", " + COL_IS_DURABLE + ", " + + COL_IS_NONCONCURRENT + ", " + COL_IS_UPDATE_DATA + ", " + + COL_REQUESTS_RECOVERY + ", " + + COL_JOB_DATAMAP + ") " + " VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - public static final String UPDATE_JOB_DETAIL = "UPDATE " + String UPDATE_JOB_DETAIL = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " SET " + COL_DESCRIPTION + " = ?, " + COL_JOB_CLASS + " = ?, " - + COL_IS_DURABLE + " = ?, " + COL_IS_VOLATILE + " = ?, " - + COL_IS_STATEFUL + " = ?, " + COL_REQUESTS_RECOVERY + " = ?, " - + COL_JOB_DATAMAP + " = ? " + " WHERE " + COL_JOB_NAME + + COL_IS_DURABLE + " = ?, " + + COL_IS_NONCONCURRENT + " = ?, " + COL_IS_UPDATE_DATA + " = ?, " + + COL_REQUESTS_RECOVERY + " = ?, " + + COL_JOB_DATAMAP + " = ? " + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String SELECT_TRIGGERS_FOR_JOB = "SELECT " + String SELECT_TRIGGERS_FOR_JOB = "SELECT " + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " - + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + COL_JOB_NAME + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String SELECT_TRIGGERS_FOR_CALENDAR = "SELECT " + String SELECT_TRIGGERS_FOR_CALENDAR = "SELECT " + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " - + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + COL_CALENDAR_NAME + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_CALENDAR_NAME + " = ?"; - public static final String SELECT_STATEFUL_JOBS_OF_TRIGGER_GROUP = "SELECT DISTINCT J." - + COL_JOB_NAME - + ", J." - + COL_JOB_GROUP - + " FROM " - + TABLE_PREFIX_SUBST - + TABLE_TRIGGERS - + " T, " - + TABLE_PREFIX_SUBST - + TABLE_JOB_DETAILS - + " J WHERE T." - + COL_TRIGGER_GROUP - + " = ? AND T." - + COL_JOB_NAME - + " = J." - + COL_JOB_NAME - + " AND T." - + COL_JOB_GROUP - + " = J." - + COL_JOB_GROUP - + " AND J." - + COL_IS_STATEFUL + " = ?"; + String DELETE_JOB_DETAIL = "DELETE FROM " + + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String DELETE_JOB_LISTENERS = "DELETE FROM " - + TABLE_PREFIX_SUBST + TABLE_JOB_LISTENERS + " WHERE " - + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; - - public static final String DELETE_JOB_DETAIL = "DELETE FROM " - + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " WHERE " + COL_JOB_NAME + String SELECT_JOB_NONCONCURRENT = "SELECT " + + COL_IS_NONCONCURRENT + " FROM " + TABLE_PREFIX_SUBST + + TABLE_JOB_DETAILS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String SELECT_JOB_STATEFUL = "SELECT " - + COL_IS_STATEFUL + " FROM " + TABLE_PREFIX_SUBST - + TABLE_JOB_DETAILS + " WHERE " + COL_JOB_NAME + " = ? AND " - + COL_JOB_GROUP + " = ?"; - - public static final String SELECT_JOB_EXISTENCE = "SELECT " + COL_JOB_NAME + String SELECT_JOB_EXISTENCE = "SELECT " + COL_JOB_NAME + " FROM " + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " WHERE " - + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String UPDATE_JOB_DATA = "UPDATE " + TABLE_PREFIX_SUBST + String UPDATE_JOB_DATA = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " SET " + COL_JOB_DATAMAP + " = ? " - + " WHERE " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; + + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String INSERT_JOB_LISTENER = "INSERT INTO " - + TABLE_PREFIX_SUBST + TABLE_JOB_LISTENERS + " (" + COL_JOB_NAME - + ", " + COL_JOB_GROUP + ", " + COL_JOB_LISTENER - + ") VALUES(?, ?, ?)"; - - public static final String SELECT_JOB_LISTENERS = "SELECT " - + COL_JOB_LISTENER + " FROM " + TABLE_PREFIX_SUBST - + TABLE_JOB_LISTENERS + " WHERE " + COL_JOB_NAME + " = ? AND " - + COL_JOB_GROUP + " = ?"; - - public static final String SELECT_JOB_DETAIL = "SELECT *" + " FROM " - + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " WHERE " + COL_JOB_NAME + String SELECT_JOB_DETAIL = "SELECT *" + " FROM " + + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; + - public static final String SELECT_NUM_JOBS = "SELECT COUNT(" + COL_JOB_NAME - + ") " + " FROM " + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS; + String SELECT_NUM_JOBS = "SELECT COUNT(" + COL_JOB_NAME + + ") " + " FROM " + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - public static final String SELECT_JOB_GROUPS = "SELECT DISTINCT(" + String SELECT_JOB_GROUPS = "SELECT DISTINCT(" + COL_JOB_GROUP + ") FROM " + TABLE_PREFIX_SUBST - + TABLE_JOB_DETAILS; + + TABLE_JOB_DETAILS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - public static final String SELECT_JOBS_IN_GROUP = "SELECT " + COL_JOB_NAME + String SELECT_JOBS_IN_GROUP_LIKE = "SELECT " + COL_JOB_NAME + ", " + COL_JOB_GROUP + " FROM " + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " WHERE " - + COL_JOB_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_GROUP + " LIKE ?"; - public static final String SELECT_VOLATILE_JOBS = "SELECT " + COL_JOB_NAME - + ", " + COL_JOB_GROUP + " FROM " + TABLE_PREFIX_SUBST - + TABLE_JOB_DETAILS + " WHERE " + COL_IS_VOLATILE + " = ?"; + String SELECT_JOBS_IN_GROUP = "SELECT " + COL_JOB_NAME + ", " + COL_JOB_GROUP + + " FROM " + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_GROUP + " = ?"; - public static final String INSERT_TRIGGER = "INSERT INTO " - + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " (" + COL_TRIGGER_NAME + String INSERT_TRIGGER = "INSERT INTO " + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " (" + COL_SCHEDULER_NAME + ", " + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", " + COL_JOB_NAME + ", " - + COL_JOB_GROUP + ", " + COL_IS_VOLATILE + ", " + COL_DESCRIPTION + + COL_JOB_GROUP + ", " + COL_DESCRIPTION + ", " + COL_NEXT_FIRE_TIME + ", " + COL_PREV_FIRE_TIME + ", " + COL_TRIGGER_STATE + ", " + COL_TRIGGER_TYPE + ", " + COL_START_TIME + ", " + COL_END_TIME + ", " + COL_CALENDAR_NAME - + ", " + COL_MISFIRE_INSTRUCTION + ", " + COL_JOB_DATAMAP + ") " - + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + ", " + COL_MISFIRE_INSTRUCTION + ", " + COL_JOB_DATAMAP + ", " + COL_PRIORITY + ") " + + " VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - public static final String INSERT_SIMPLE_TRIGGER = "INSERT INTO " + String INSERT_SIMPLE_TRIGGER = "INSERT INTO " + TABLE_PREFIX_SUBST + TABLE_SIMPLE_TRIGGERS + " (" + + COL_SCHEDULER_NAME + ", " + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", " + COL_REPEAT_COUNT + ", " + COL_REPEAT_INTERVAL + ", " - + COL_TIMES_TRIGGERED + ") " + " VALUES(?, ?, ?, ?, ?)"; + + COL_TIMES_TRIGGERED + ") " + " VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?, ?, ?)"; - public static final String INSERT_CRON_TRIGGER = "INSERT INTO " + String INSERT_CRON_TRIGGER = "INSERT INTO " + TABLE_PREFIX_SUBST + TABLE_CRON_TRIGGERS + " (" + + COL_SCHEDULER_NAME + ", " + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", " + COL_CRON_EXPRESSION + ", " + COL_TIME_ZONE_ID + ") " - + " VALUES(?, ?, ?, ?)"; + + " VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?, ?)"; - public static final String INSERT_BLOB_TRIGGER = "INSERT INTO " + String INSERT_BLOB_TRIGGER = "INSERT INTO " + TABLE_PREFIX_SUBST + TABLE_BLOB_TRIGGERS + " (" + + COL_SCHEDULER_NAME + ", " + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", " + COL_BLOB - + ") " + " VALUES(?, ?, ?)"; + + ") " + " VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?)"; - public static final String UPDATE_TRIGGER_SKIP_DATA = "UPDATE " + TABLE_PREFIX_SUBST + String UPDATE_TRIGGER_SKIP_DATA = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_JOB_NAME + " = ?, " - + COL_JOB_GROUP + " = ?, " + COL_IS_VOLATILE + " = ?, " + + COL_JOB_GROUP + " = ?, " + COL_DESCRIPTION + " = ?, " + COL_NEXT_FIRE_TIME + " = ?, " + COL_PREV_FIRE_TIME + " = ?, " + COL_TRIGGER_STATE + " = ?, " + COL_TRIGGER_TYPE + " = ?, " + COL_START_TIME + " = ?, " + COL_END_TIME + " = ?, " + COL_CALENDAR_NAME + " = ?, " - + COL_MISFIRE_INSTRUCTION + " = ? WHERE " + COL_TRIGGER_NAME + + COL_MISFIRE_INSTRUCTION + " = ?, " + COL_PRIORITY + + " = ? WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String UPDATE_TRIGGER = "UPDATE " + TABLE_PREFIX_SUBST + String UPDATE_TRIGGER = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_JOB_NAME + " = ?, " - + COL_JOB_GROUP + " = ?, " + COL_IS_VOLATILE + " = ?, " + + COL_JOB_GROUP + " = ?, " + COL_DESCRIPTION + " = ?, " + COL_NEXT_FIRE_TIME + " = ?, " + COL_PREV_FIRE_TIME + " = ?, " + COL_TRIGGER_STATE + " = ?, " + COL_TRIGGER_TYPE + " = ?, " + COL_START_TIME + " = ?, " + COL_END_TIME + " = ?, " + COL_CALENDAR_NAME + " = ?, " - + COL_MISFIRE_INSTRUCTION + " = ?, " + COL_JOB_DATAMAP + " = ? WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_MISFIRE_INSTRUCTION + " = ?, " + COL_PRIORITY + " = ?, " + + COL_JOB_DATAMAP + " = ? WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String UPDATE_SIMPLE_TRIGGER = "UPDATE " + String UPDATE_SIMPLE_TRIGGER = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_SIMPLE_TRIGGERS + " SET " + COL_REPEAT_COUNT + " = ?, " + COL_REPEAT_INTERVAL + " = ?, " - + COL_TIMES_TRIGGERED + " = ? WHERE " + COL_TRIGGER_NAME + + COL_TIMES_TRIGGERED + " = ? WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String UPDATE_CRON_TRIGGER = "UPDATE " + String UPDATE_CRON_TRIGGER = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_CRON_TRIGGERS + " SET " - + COL_CRON_EXPRESSION + " = ? WHERE " + COL_TRIGGER_NAME + + COL_CRON_EXPRESSION + " = ?, " + COL_TIME_ZONE_ID + + " = ? WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String UPDATE_BLOB_TRIGGER = "UPDATE " + String UPDATE_BLOB_TRIGGER = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_BLOB_TRIGGERS + " SET " + COL_BLOB - + " = ? WHERE " + COL_TRIGGER_NAME + " = ? AND " + + " = ? WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_TRIGGER_EXISTENCE = "SELECT " + String SELECT_TRIGGER_EXISTENCE = "SELECT " + COL_TRIGGER_NAME + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS - + " WHERE " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String UPDATE_TRIGGER_STATE = "UPDATE " + String UPDATE_TRIGGER_STATE = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_TRIGGER_STATE - + " = ?" + " WHERE " + COL_TRIGGER_NAME + " = ? AND " + + " = ?" + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String UPDATE_TRIGGER_STATE_FROM_STATE = "UPDATE " + String UPDATE_TRIGGER_STATE_FROM_STATE = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_TRIGGER_STATE - + " = ?" + " WHERE " + COL_TRIGGER_NAME + " = ? AND " + + " = ?" + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ? AND " + COL_TRIGGER_STATE + " = ?"; - public static final String UPDATE_TRIGGER_GROUP_STATE = "UPDATE " - + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_TRIGGER_STATE - + " = ?"; - - public static final String UPDATE_TRIGGER_GROUP_STATE_FROM_STATE = "UPDATE " + String UPDATE_TRIGGER_GROUP_STATE_FROM_STATE = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_TRIGGER_STATE + " = ?" + " WHERE " - + COL_TRIGGER_GROUP - + " = ? AND " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_GROUP + + " LIKE ? AND " + COL_TRIGGER_STATE + " = ?"; - public static final String UPDATE_TRIGGER_STATE_FROM_STATES = "UPDATE " + String UPDATE_TRIGGER_STATE_FROM_STATES = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_TRIGGER_STATE - + " = ?" + " WHERE " + COL_TRIGGER_NAME + " = ? AND " + + " = ?" + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ? AND (" + COL_TRIGGER_STATE + " = ? OR " + COL_TRIGGER_STATE + " = ? OR " + COL_TRIGGER_STATE + " = ?)"; - public static final String UPDATE_TRIGGER_GROUP_STATE_FROM_STATES = "UPDATE " + String UPDATE_TRIGGER_GROUP_STATE_FROM_STATES = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_TRIGGER_STATE + " = ?" + " WHERE " - + COL_TRIGGER_GROUP - + " = ? AND (" + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_GROUP + + " LIKE ? AND (" + COL_TRIGGER_STATE + " = ? OR " + COL_TRIGGER_STATE + " = ? OR " + COL_TRIGGER_STATE + " = ?)"; - public static final String UPDATE_JOB_TRIGGER_STATES = "UPDATE " + String UPDATE_JOB_TRIGGER_STATES = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_TRIGGER_STATE - + " = ? WHERE " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + + " = ? WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String UPDATE_JOB_TRIGGER_STATES_FROM_OTHER_STATE = "UPDATE " + String UPDATE_JOB_TRIGGER_STATES_FROM_OTHER_STATE = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_TRIGGER_STATE + " = ? WHERE " - + COL_JOB_NAME + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ? AND " + COL_TRIGGER_STATE + " = ?"; - public static final String DELETE_TRIGGER_LISTENERS = "DELETE FROM " - + TABLE_PREFIX_SUBST + TABLE_TRIGGER_LISTENERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - - public static final String INSERT_TRIGGER_LISTENER = "INSERT INTO " - + TABLE_PREFIX_SUBST + TABLE_TRIGGER_LISTENERS + " (" - + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", " - + COL_TRIGGER_LISTENER + ") VALUES(?, ?, ?)"; - - public static final String SELECT_TRIGGER_LISTENERS = "SELECT " - + COL_TRIGGER_LISTENER + " FROM " + TABLE_PREFIX_SUBST - + TABLE_TRIGGER_LISTENERS + " WHERE " + COL_TRIGGER_NAME - + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - - public static final String DELETE_SIMPLE_TRIGGER = "DELETE FROM " + String DELETE_SIMPLE_TRIGGER = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_SIMPLE_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String DELETE_CRON_TRIGGER = "DELETE FROM " + String DELETE_CRON_TRIGGER = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_CRON_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String DELETE_BLOB_TRIGGER = "DELETE FROM " + String DELETE_BLOB_TRIGGER = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_BLOB_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String DELETE_TRIGGER = "DELETE FROM " + String DELETE_TRIGGER = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_NUM_TRIGGERS_FOR_JOB = "SELECT COUNT(" + String SELECT_NUM_TRIGGERS_FOR_JOB = "SELECT COUNT(" + COL_TRIGGER_NAME + ") FROM " + TABLE_PREFIX_SUBST - + TABLE_TRIGGERS + " WHERE " + COL_JOB_NAME + " = ? AND " + + TABLE_TRIGGERS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String SELECT_JOB_FOR_TRIGGER = "SELECT J." + String SELECT_JOB_FOR_TRIGGER = "SELECT J." + COL_JOB_NAME + ", J." + COL_JOB_GROUP + ", J." + COL_IS_DURABLE + ", J." + COL_JOB_CLASS + ", J." + COL_REQUESTS_RECOVERY + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " T, " + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS - + " J WHERE T." + COL_TRIGGER_NAME + " = ? AND T." + + " J WHERE T." + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND J." + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND T." + COL_TRIGGER_NAME + " = ? AND T." + COL_TRIGGER_GROUP + " = ? AND T." + COL_JOB_NAME + " = J." + COL_JOB_NAME + " AND T." + COL_JOB_GROUP + " = J." + COL_JOB_GROUP; - public static final String SELECT_TRIGGER = "SELECT *" + " FROM " + String SELECT_TRIGGER = "SELECT * FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_TRIGGER_DATA = "SELECT " + + String SELECT_TRIGGER_DATA = "SELECT " + COL_JOB_DATAMAP + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_TRIGGER_STATE = "SELECT " + String SELECT_TRIGGER_STATE = "SELECT " + COL_TRIGGER_STATE + " FROM " + TABLE_PREFIX_SUBST - + TABLE_TRIGGERS + " WHERE " + COL_TRIGGER_NAME + " = ? AND " + + TABLE_TRIGGERS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_TRIGGER_STATUS = "SELECT " + String SELECT_TRIGGER_STATUS = "SELECT " + COL_TRIGGER_STATE + ", " + COL_NEXT_FIRE_TIME + ", " + COL_JOB_NAME + ", " + COL_JOB_GROUP + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_SIMPLE_TRIGGER = "SELECT *" + " FROM " + String SELECT_SIMPLE_TRIGGER = "SELECT *" + " FROM " + TABLE_PREFIX_SUBST + TABLE_SIMPLE_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_CRON_TRIGGER = "SELECT *" + " FROM " + String SELECT_CRON_TRIGGER = "SELECT *" + " FROM " + TABLE_PREFIX_SUBST + TABLE_CRON_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_BLOB_TRIGGER = "SELECT *" + " FROM " + String SELECT_BLOB_TRIGGER = "SELECT *" + " FROM " + TABLE_PREFIX_SUBST + TABLE_BLOB_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_NUM_TRIGGERS = "SELECT COUNT(" + String SELECT_NUM_TRIGGERS = "SELECT COUNT(" + COL_TRIGGER_NAME + ") " + " FROM " + TABLE_PREFIX_SUBST - + TABLE_TRIGGERS; + + TABLE_TRIGGERS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - public static final String SELECT_NUM_TRIGGERS_IN_GROUP = "SELECT COUNT(" + String SELECT_NUM_TRIGGERS_IN_GROUP = "SELECT COUNT(" + COL_TRIGGER_NAME + ") " + " FROM " + TABLE_PREFIX_SUBST - + TABLE_TRIGGERS + " WHERE " + COL_TRIGGER_GROUP + " = ?"; + + TABLE_TRIGGERS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_TRIGGER_GROUPS = "SELECT DISTINCT(" + String SELECT_TRIGGER_GROUPS = "SELECT DISTINCT(" + COL_TRIGGER_GROUP + ") FROM " + TABLE_PREFIX_SUBST - + TABLE_TRIGGERS; + + TABLE_TRIGGERS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - public static final String SELECT_TRIGGERS_IN_GROUP = "SELECT " - + COL_TRIGGER_NAME + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS - + " WHERE " + COL_TRIGGER_GROUP + " = ?"; + String SELECT_TRIGGER_GROUPS_FILTERED = "SELECT DISTINCT(" + + COL_TRIGGER_GROUP + ") FROM " + TABLE_PREFIX_SUBST + + TABLE_TRIGGERS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + " AND " + COL_TRIGGER_GROUP + " LIKE ?"; - public static final String INSERT_CALENDAR = "INSERT INTO " - + TABLE_PREFIX_SUBST + TABLE_CALENDARS + " (" + COL_CALENDAR_NAME - + ", " + COL_CALENDAR + ") " + " VALUES(?, ?)"; + String SELECT_TRIGGERS_IN_GROUP_LIKE = "SELECT " + + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_GROUP + " LIKE ?"; - public static final String UPDATE_CALENDAR = "UPDATE " + TABLE_PREFIX_SUBST + String SELECT_TRIGGERS_IN_GROUP = "SELECT " + + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_GROUP + " = ?"; + + String INSERT_CALENDAR = "INSERT INTO " + + TABLE_PREFIX_SUBST + TABLE_CALENDARS + " (" + COL_SCHEDULER_NAME + ", " + COL_CALENDAR_NAME + + ", " + COL_CALENDAR + ") " + " VALUES(" + SCHED_NAME_SUBST + ", ?, ?)"; + + String UPDATE_CALENDAR = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_CALENDARS + " SET " + COL_CALENDAR + " = ? " + " WHERE " - + COL_CALENDAR_NAME + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_CALENDAR_NAME + " = ?"; - public static final String SELECT_CALENDAR_EXISTENCE = "SELECT " + String SELECT_CALENDAR_EXISTENCE = "SELECT " + COL_CALENDAR_NAME + " FROM " + TABLE_PREFIX_SUBST - + TABLE_CALENDARS + " WHERE " + COL_CALENDAR_NAME + " = ?"; + + TABLE_CALENDARS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_CALENDAR_NAME + " = ?"; - public static final String SELECT_CALENDAR = "SELECT *" + " FROM " + String SELECT_CALENDAR = "SELECT *" + " FROM " + TABLE_PREFIX_SUBST + TABLE_CALENDARS + " WHERE " - + COL_CALENDAR_NAME + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_CALENDAR_NAME + " = ?"; - public static final String SELECT_REFERENCED_CALENDAR = "SELECT " + String SELECT_REFERENCED_CALENDAR = "SELECT " + COL_CALENDAR_NAME + " FROM " + TABLE_PREFIX_SUBST - + TABLE_TRIGGERS + " WHERE " + COL_CALENDAR_NAME + " = ?"; + + TABLE_TRIGGERS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_CALENDAR_NAME + " = ?"; - public static final String DELETE_CALENDAR = "DELETE FROM " + String DELETE_CALENDAR = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_CALENDARS + " WHERE " - + COL_CALENDAR_NAME + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_CALENDAR_NAME + " = ?"; - public static final String SELECT_NUM_CALENDARS = "SELECT COUNT(" + String SELECT_NUM_CALENDARS = "SELECT COUNT(" + COL_CALENDAR_NAME + ") " + " FROM " + TABLE_PREFIX_SUBST - + TABLE_CALENDARS; + + TABLE_CALENDARS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - public static final String SELECT_CALENDARS = "SELECT " + COL_CALENDAR_NAME - + " FROM " + TABLE_PREFIX_SUBST + TABLE_CALENDARS; + String SELECT_CALENDARS = "SELECT " + COL_CALENDAR_NAME + + " FROM " + TABLE_PREFIX_SUBST + TABLE_CALENDARS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - public static final String SELECT_NEXT_FIRE_TIME = "SELECT MIN(" + String SELECT_NEXT_FIRE_TIME = "SELECT MIN(" + COL_NEXT_FIRE_TIME + ") AS " + ALIAS_COL_NEXT_FIRE_TIME + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " - + COL_TRIGGER_STATE + " = ? AND " + COL_NEXT_FIRE_TIME + " >= 0"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_STATE + " = ? AND " + COL_NEXT_FIRE_TIME + " >= 0"; - public static final String SELECT_TRIGGER_FOR_FIRE_TIME = "SELECT " + String SELECT_TRIGGER_FOR_FIRE_TIME = "SELECT " + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + " FROM " + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " - + COL_TRIGGER_STATE + " = ? AND " + COL_NEXT_FIRE_TIME + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_STATE + " = ? AND " + COL_NEXT_FIRE_TIME + " = ?"; - public static final String INSERT_FIRED_TRIGGER = "INSERT INTO " - + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " (" + COL_ENTRY_ID + String SELECT_NEXT_TRIGGER_TO_ACQUIRE = "SELECT " + + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", " + + COL_NEXT_FIRE_TIME + ", " + COL_PRIORITY + " FROM " + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_STATE + " = ? AND " + COL_NEXT_FIRE_TIME + " <= ? " + + "AND (" + COL_MISFIRE_INSTRUCTION + " = -1 OR (" +COL_MISFIRE_INSTRUCTION+ " != -1 AND "+ COL_NEXT_FIRE_TIME + " >= ?)) " + + "ORDER BY "+ COL_NEXT_FIRE_TIME + " ASC, " + COL_PRIORITY + " DESC"; + + + String INSERT_FIRED_TRIGGER = "INSERT INTO " + + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " (" + COL_SCHEDULER_NAME + ", " + COL_ENTRY_ID + ", " + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", " - + COL_IS_VOLATILE + ", " + COL_INSTANCE_NAME + ", " - + COL_FIRED_TIME + ", " + COL_ENTRY_STATE + ", " + COL_JOB_NAME - + ", " + COL_JOB_GROUP + ", " + COL_IS_STATEFUL + ", " - + COL_REQUESTS_RECOVERY - + ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + COL_INSTANCE_NAME + ", " + + COL_FIRED_TIME + ", " + COL_SCHED_TIME + ", " + COL_ENTRY_STATE + ", " + COL_JOB_NAME + + ", " + COL_JOB_GROUP + ", " + COL_IS_NONCONCURRENT + ", " + + COL_REQUESTS_RECOVERY + ", " + COL_PRIORITY + + ") VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - public static final String UPDATE_INSTANCES_FIRED_TRIGGER_STATE = "UPDATE " - + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " SET " - + COL_ENTRY_STATE + " = ? AND " + COL_FIRED_TIME + " = ? WHERE " - + COL_INSTANCE_NAME + " = ?"; + String UPDATE_FIRED_TRIGGER = "UPDATE " + + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " SET " + + COL_INSTANCE_NAME + " = ?, " + + COL_FIRED_TIME + " = ?, " + COL_SCHED_TIME + " = ?, " + COL_ENTRY_STATE + " = ?, " + COL_JOB_NAME + + " = ?, " + COL_JOB_GROUP + " = ?, " + COL_IS_NONCONCURRENT + " = ?, " + + COL_REQUESTS_RECOVERY + " = ? WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_ENTRY_ID + " = ?"; - public static final String SELECT_INSTANCES_FIRED_TRIGGERS = "SELECT * FROM " + String SELECT_INSTANCES_FIRED_TRIGGERS = "SELECT * FROM " + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_INSTANCE_NAME + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_INSTANCE_NAME + " = ?"; - public static final String SELECT_INSTANCES_RECOVERABLE_FIRED_TRIGGERS = "SELECT * FROM " + String SELECT_INSTANCES_RECOVERABLE_FIRED_TRIGGERS = "SELECT * FROM " + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_INSTANCE_NAME + " = ? AND " + COL_REQUESTS_RECOVERY + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_INSTANCE_NAME + " = ? AND " + COL_REQUESTS_RECOVERY + " = ?"; - public static final String SELECT_JOB_EXECUTION_COUNT = "SELECT COUNT(" + String SELECT_JOB_EXECUTION_COUNT = "SELECT COUNT(" + COL_TRIGGER_NAME + ") FROM " + TABLE_PREFIX_SUBST - + TABLE_FIRED_TRIGGERS + " WHERE " + COL_JOB_NAME + " = ? AND " + + TABLE_FIRED_TRIGGERS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String SELECT_FIRED_TRIGGERS = "SELECT * FROM " - + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS; + String SELECT_FIRED_TRIGGERS = "SELECT * FROM " + + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - public static final String SELECT_FIRED_TRIGGER = "SELECT * FROM " + String SELECT_FIRED_TRIGGER = "SELECT * FROM " + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_FIRED_TRIGGER_GROUP = "SELECT * FROM " + String SELECT_FIRED_TRIGGER_GROUP = "SELECT * FROM " + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_FIRED_TRIGGERS_OF_JOB = "SELECT * FROM " + String SELECT_FIRED_TRIGGERS_OF_JOB = "SELECT * FROM " + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; - public static final String SELECT_FIRED_TRIGGERS_OF_JOB_GROUP = "SELECT * FROM " + String SELECT_FIRED_TRIGGERS_OF_JOB_GROUP = "SELECT * FROM " + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_JOB_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_GROUP + " = ?"; - public static final String DELETE_FIRED_TRIGGER = "DELETE FROM " + String DELETE_FIRED_TRIGGER = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_ENTRY_ID + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_ENTRY_ID + " = ?"; - public static final String DELETE_INSTANCES_FIRED_TRIGGERS = "DELETE FROM " + String DELETE_INSTANCES_FIRED_TRIGGERS = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_INSTANCE_NAME + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_INSTANCE_NAME + " = ?"; - public static final String DELETE_VOLATILE_FIRED_TRIGGERS = "DELETE FROM " - + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_IS_VOLATILE + " = ?"; - - public static final String DELETE_NO_RECOVERY_FIRED_TRIGGERS = "DELETE FROM " + String DELETE_NO_RECOVERY_FIRED_TRIGGERS = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_FIRED_TRIGGERS + " WHERE " - + COL_INSTANCE_NAME + " = ?" + COL_REQUESTS_RECOVERY + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_INSTANCE_NAME + " = ?" + COL_REQUESTS_RECOVERY + " = ?"; - public static final String INSERT_SCHEDULER_STATE = "INSERT INTO " + String DELETE_ALL_SIMPLE_TRIGGERS = "DELETE FROM " + TABLE_PREFIX_SUBST + "SIMPLE_TRIGGERS " + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + String DELETE_ALL_SIMPROP_TRIGGERS = "DELETE FROM " + TABLE_PREFIX_SUBST + "SIMPROP_TRIGGERS " + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + String DELETE_ALL_CRON_TRIGGERS = "DELETE FROM " + TABLE_PREFIX_SUBST + "CRON_TRIGGERS" + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + String DELETE_ALL_BLOB_TRIGGERS = "DELETE FROM " + TABLE_PREFIX_SUBST + "BLOB_TRIGGERS" + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + String DELETE_ALL_TRIGGERS = "DELETE FROM " + TABLE_PREFIX_SUBST + "TRIGGERS" + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + String DELETE_ALL_JOB_DETAILS = "DELETE FROM " + TABLE_PREFIX_SUBST + "JOB_DETAILS" + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + String DELETE_ALL_CALENDARS = "DELETE FROM " + TABLE_PREFIX_SUBST + "CALENDARS" + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + String DELETE_ALL_PAUSED_TRIGGER_GRPS = "DELETE FROM " + TABLE_PREFIX_SUBST + "PAUSED_TRIGGER_GRPS" + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + + String SELECT_FIRED_TRIGGER_INSTANCE_NAMES = + "SELECT DISTINCT " + COL_INSTANCE_NAME + " FROM " + + TABLE_PREFIX_SUBST + + TABLE_FIRED_TRIGGERS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; + + String INSERT_SCHEDULER_STATE = "INSERT INTO " + TABLE_PREFIX_SUBST + TABLE_SCHEDULER_STATE + " (" + + COL_SCHEDULER_NAME + ", " + COL_INSTANCE_NAME + ", " + COL_LAST_CHECKIN_TIME + ", " - + COL_CHECKIN_INTERVAL + ", " + COL_RECOVERER - + ") VALUES(?, ?, ?, ?)"; + + COL_CHECKIN_INTERVAL + ") VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?)"; - public static final String SELECT_SCHEDULER_STATE = "SELECT * FROM " + String SELECT_SCHEDULER_STATE = "SELECT * FROM " + TABLE_PREFIX_SUBST + TABLE_SCHEDULER_STATE + " WHERE " - + COL_INSTANCE_NAME + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_INSTANCE_NAME + " = ?"; - public static final String SELECT_SCHEDULER_STATES = "SELECT * FROM " - + TABLE_PREFIX_SUBST + TABLE_SCHEDULER_STATE; + String SELECT_SCHEDULER_STATES = "SELECT * FROM " + + TABLE_PREFIX_SUBST + TABLE_SCHEDULER_STATE + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - public static final String DELETE_SCHEDULER_STATE = "DELETE FROM " + String DELETE_SCHEDULER_STATE = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_SCHEDULER_STATE + " WHERE " - + COL_INSTANCE_NAME + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_INSTANCE_NAME + " = ?"; - public static final String UPDATE_SCHEDULER_STATE = "UPDATE " + String UPDATE_SCHEDULER_STATE = "UPDATE " + TABLE_PREFIX_SUBST + TABLE_SCHEDULER_STATE + " SET " - + COL_LAST_CHECKIN_TIME + " = ?, " + COL_RECOVERER + " = ? WHERE " - + COL_INSTANCE_NAME + " = ?"; + + COL_LAST_CHECKIN_TIME + " = ? WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_INSTANCE_NAME + " = ?"; - public static final String INSERT_PAUSED_TRIGGER_GROUP = "INSERT INTO " + String INSERT_PAUSED_TRIGGER_GROUP = "INSERT INTO " + TABLE_PREFIX_SUBST + TABLE_PAUSED_TRIGGERS + " (" - + COL_TRIGGER_GROUP + ") VALUES(?)"; + + COL_SCHEDULER_NAME + ", " + + COL_TRIGGER_GROUP + ") VALUES(" + SCHED_NAME_SUBST + ", ?)"; - public static final String SELECT_PAUSED_TRIGGER_GROUP = "SELECT " + String SELECT_PAUSED_TRIGGER_GROUP = "SELECT " + COL_TRIGGER_GROUP + " FROM " + TABLE_PREFIX_SUBST - + TABLE_PAUSED_TRIGGERS + " WHERE " + COL_TRIGGER_GROUP + " = ?"; + + TABLE_PAUSED_TRIGGERS + " WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_GROUP + " = ?"; - public static final String SELECT_PAUSED_TRIGGER_GROUPS = "SELECT " + String SELECT_PAUSED_TRIGGER_GROUPS = "SELECT " + COL_TRIGGER_GROUP + " FROM " + TABLE_PREFIX_SUBST - + TABLE_PAUSED_TRIGGERS; + + TABLE_PAUSED_TRIGGERS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; - public static final String DELETE_PAUSED_TRIGGER_GROUP = "DELETE FROM " + String DELETE_PAUSED_TRIGGER_GROUP = "DELETE FROM " + TABLE_PREFIX_SUBST + TABLE_PAUSED_TRIGGERS + " WHERE " - + COL_TRIGGER_GROUP + " = ?"; + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_GROUP + " LIKE ?"; - public static final String DELETE_PAUSED_TRIGGER_GROUPS = "DELETE FROM " - + TABLE_PREFIX_SUBST + TABLE_PAUSED_TRIGGERS; + String DELETE_PAUSED_TRIGGER_GROUPS = "DELETE FROM " + + TABLE_PREFIX_SUBST + TABLE_PAUSED_TRIGGERS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST; // CREATE TABLE qrtz_scheduler_state(INSTANCE_NAME VARCHAR2(80) NOT NULL, // LAST_CHECKIN_TIME NUMBER(13) NOT NULL, CHECKIN_INTERVAL NUMBER(13) NOT - // NULL, RECOVERER VARCHAR2(80) NOT NULL, PRIMARY KEY (INSTANCE_NAME)); + // NULL, PRIMARY KEY (INSTANCE_NAME)); } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/StdJDBCDelegate.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/StdJDBCDelegate.java (.../StdJDBCDelegate.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/StdJDBCDelegate.java (.../StdJDBCDelegate.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,15 +15,17 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; +import static org.quartz.JobKey.jobKey; +import static org.quartz.TriggerBuilder.newTrigger; +import static org.quartz.TriggerKey.triggerKey; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.math.BigDecimal; @@ -32,29 +34,36 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; +import java.sql.Statement; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Iterator; -import java.util.HashMap; -import java.util.Set; import java.util.Properties; -import java.util.TimeZone; +import java.util.Set; -import org.apache.commons.logging.Log; import org.quartz.Calendar; -import org.quartz.CronTrigger; +import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.JobPersistenceException; import org.quartz.Scheduler; import org.quartz.SimpleTrigger; import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import org.quartz.impl.JobDetailImpl; +import org.quartz.impl.jdbcjobstore.TriggerPersistenceDelegate.TriggerPropertyBundle; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.impl.matchers.StringMatcher; +import org.quartz.impl.triggers.SimpleTriggerImpl; import org.quartz.spi.ClassLoadHelper; -import org.quartz.utils.Key; -import org.quartz.utils.TriggerStatus; +import org.quartz.spi.OperableTrigger; +import org.slf4j.Logger; /** *

@@ -65,6 +74,7 @@ * * @author Jeffrey Wescott * @author James House + * @author Eric Mueller */ public class StdJDBCDelegate implements DriverDelegate, StdJDBCConstants { @@ -76,14 +86,21 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - protected Log logger = null; + protected Logger logger = null; protected String tablePrefix = DEFAULT_TABLE_PREFIX; protected String instanceId; + protected String schedName; + protected boolean useProperties; + + protected ClassLoadHelper classLoadHelper; + protected List triggerPersistenceDelegates = new LinkedList(); + + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -96,48 +113,96 @@ *

* 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; + public StdJDBCDelegate() { } - /** - *

- * 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. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + /** + * @param initString of the format: settingName=settingValue|otherSettingName=otherSettingValue|... + * @throws NoSuchDelegateException + */ + public void initialize(Logger logger, String tablePrefix, String schedName, String instanceId, ClassLoadHelper classLoadHelper, boolean useProperties, String initString) throws NoSuchDelegateException { + this.logger = logger; + this.tablePrefix = tablePrefix; + this.schedName = schedName; + this.instanceId = instanceId; + this.useProperties = useProperties; + this.classLoadHelper = classLoadHelper; + addDefaultTriggerPersistenceDelegates(); + + if(initString == null) + return; + + String[] settings = initString.split("\\|"); + + for(String setting: settings) { + String[] parts = setting.split("="); + String name = parts[0]; + if(parts.length == 1 || parts[1] == null || parts[1].equals("")) + continue; + + if(name.equals("triggerPersistenceDelegateClasses")) { + + String[] trigDelegates = parts[1].split(","); + + for(String trigDelClassName: trigDelegates) { + try { + Class trigDelClass = classLoadHelper.loadClass(trigDelClassName); + addTriggerPersistenceDelegate((TriggerPersistenceDelegate) trigDelClass.newInstance()); + } catch (Exception e) { + throw new NoSuchDelegateException("Error instantiating TriggerPersistenceDelegate of type: " + trigDelClassName, e); + } + } + } + else + throw new NoSuchDelegateException("Unknown setting: '" + name + "'"); + } + } + + protected void addDefaultTriggerPersistenceDelegates() { + addTriggerPersistenceDelegate(new SimpleTriggerPersistenceDelegate()); + addTriggerPersistenceDelegate(new CronTriggerPersistenceDelegate()); + addTriggerPersistenceDelegate(new CalendarIntervalTriggerPersistenceDelegate()); + addTriggerPersistenceDelegate(new DailyTimeIntervalTriggerPersistenceDelegate()); + } + protected boolean canUseProperties() { return useProperties; } + + public void addTriggerPersistenceDelegate(TriggerPersistenceDelegate delegate) { + logger.debug("Adding TriggerPersistenceDelegate of type: " + delegate.getClass().getCanonicalName()); + delegate.initialize(tablePrefix, schedName); + this.triggerPersistenceDelegates.add(delegate); + } + + public TriggerPersistenceDelegate findTriggerPersistenceDelegate(OperableTrigger trigger) { + for(TriggerPersistenceDelegate delegate: triggerPersistenceDelegates) { + if(delegate.canHandleTriggerType(trigger)) + return delegate; + } + + return null; + } + public TriggerPersistenceDelegate findTriggerPersistenceDelegate(String discriminator) { + for(TriggerPersistenceDelegate delegate: triggerPersistenceDelegates) { + if(delegate.getHandledTriggerTypeDiscriminator().equals(discriminator)) + return delegate; + } + + return null; + } + //--------------------------------------------------------------------------- // startup / recovery //--------------------------------------------------------------------------- @@ -159,7 +224,7 @@ */ public int updateTriggerStatesFromOtherStates(Connection conn, String newState, String oldState1, String oldState2) - throws SQLException { + throws SQLException { PreparedStatement ps = null; try { @@ -170,12 +235,7 @@ ps.setString(3, oldState2); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -189,8 +249,8 @@ * @return an array of {@link * org.quartz.utils.Key} objects */ - public Key[] selectMisfiredTriggers(Connection conn, long ts) - throws SQLException { + public List selectMisfiredTriggers(Connection conn, long ts) + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -199,29 +259,16 @@ ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts))); rs = ps.executeQuery(); - ArrayList list = new ArrayList(); + LinkedList list = new LinkedList(); while (rs.next()) { String triggerName = rs.getString(COL_TRIGGER_NAME); String groupName = rs.getString(COL_TRIGGER_GROUP); - list.add(new Key(triggerName, groupName)); + list.add(triggerKey(triggerName, groupName)); } - Object[] oArr = list.toArray(); - Key[] kArr = new Key[oArr.length]; - System.arraycopy(oArr, 0, kArr, 0, oArr.length); - return kArr; + return list; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -236,8 +283,8 @@ * the state the triggers must be in * @return an array of trigger Key s */ - public Key[] selectTriggersInState(Connection conn, String state) - throws SQLException { + public List selectTriggersInState(Connection conn, String state) + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -246,30 +293,19 @@ ps.setString(1, state); rs = ps.executeQuery(); - ArrayList list = new ArrayList(); + LinkedList list = new LinkedList(); while (rs.next()) { - list.add(new Key(rs.getString(1), rs.getString(2))); + list.add(triggerKey(rs.getString(1), rs.getString(2))); } - Key[] sArr = (Key[]) list.toArray(new Key[list.size()]); - return sArr; + return list; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } - public Key[] selectMisfiredTriggersInState(Connection conn, String state, + public List selectMisfiredTriggersInState(Connection conn, String state, long ts) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -280,29 +316,90 @@ ps.setString(2, state); rs = ps.executeQuery(); - ArrayList list = new ArrayList(); + LinkedList list = new LinkedList(); while (rs.next()) { String triggerName = rs.getString(COL_TRIGGER_NAME); String groupName = rs.getString(COL_TRIGGER_GROUP); - list.add(new Key(triggerName, groupName)); + list.add(triggerKey(triggerName, groupName)); } - Object[] oArr = list.toArray(); - Key[] kArr = new Key[oArr.length]; - System.arraycopy(oArr, 0, kArr, 0, oArr.length); - return kArr; + return list; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *

+ * Get the names of all of the triggers in the given state 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 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 hasMisfiredTriggersInState(Connection conn, String state1, + long ts, int count, List resultList) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_HAS_MISFIRED_TRIGGERS_IN_STATE)); + ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts))); + ps.setString(2, state1); + 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(triggerKey(triggerName, groupName)); } } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } + + 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 countMisfiredTriggersInState( + Connection conn, String state1, long ts) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(COUNT_MISFIRED_TRIGGERS_IN_STATE)); + ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts))); + ps.setString(2, state1); + rs = ps.executeQuery(); + + if (rs.next()) { + return rs.getInt(1); } + + throw new SQLException("No misfired trigger count returned."); + } finally { + closeResultSet(rs); + closeStatement(ps); } } @@ -317,7 +414,7 @@ * @return an array of {@link * org.quartz.utils.Key} objects */ - public Key[] selectMisfiredTriggersInGroupInState(Connection conn, + public List selectMisfiredTriggersInGroupInState(Connection conn, String groupName, String state, long ts) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -330,28 +427,15 @@ ps.setString(3, state); rs = ps.executeQuery(); - ArrayList list = new ArrayList(); + LinkedList list = new LinkedList(); while (rs.next()) { String triggerName = rs.getString(COL_TRIGGER_NAME); - list.add(new Key(triggerName, groupName)); + list.add(triggerKey(triggerName, groupName)); } - Object[] oArr = list.toArray(); - Key[] kArr = new Key[oArr.length]; - System.arraycopy(oArr, 0, kArr, 0, oArr.length); - return kArr; + return list; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -376,59 +460,50 @@ * the DB Connection * @return an array of {@link org.quartz.Trigger} objects */ - public Trigger[] selectTriggersForRecoveringJobs(Connection conn) - throws SQLException, IOException, ClassNotFoundException { + public List 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); - ps.setBoolean(2, true); + setBoolean(ps, 2, true); rs = ps.executeQuery(); long dumId = System.currentTimeMillis(); - ArrayList list = new ArrayList(); + LinkedList list = new LinkedList(); 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); - SimpleTrigger rcvryTrig = new SimpleTrigger("recover_" + long scheduledTime = rs.getLong(COL_SCHED_TIME); + int priority = rs.getInt(COL_PRIORITY); + @SuppressWarnings("deprecation") + SimpleTriggerImpl rcvryTrig = new SimpleTriggerImpl("recover_" + instanceId + "_" + String.valueOf(dumId++), - Scheduler.DEFAULT_RECOVERY_GROUP, new Date(firedTime)); + Scheduler.DEFAULT_RECOVERY_GROUP, new Date(scheduledTime)); rcvryTrig.setJobName(jobName); rcvryTrig.setJobGroup(jobGroup); - rcvryTrig - .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW); + rcvryTrig.setPriority(priority); + rcvryTrig.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY); JobDataMap jd = selectTriggerJobDataMap(conn, trigName, trigGroup); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_NAME", trigName); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_GROUP", trigGroup); - jd.put("QRTZ_FAILED_JOB_ORIG_TRIGGER_FIRETIME_IN_MILLISECONDS_AS_STRING", String.valueOf(firedTime)); + 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)); + jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_SCHEDULED_FIRETIME_IN_MILLISECONDS, String.valueOf(scheduledTime)); 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; + return list; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -449,34 +524,66 @@ return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } - public int deleteFiredTriggers(Connection conn, String instanceId) - throws SQLException { + public int deleteFiredTriggers(Connection conn, String theInstanceId) + throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_INSTANCES_FIRED_TRIGGERS)); - ps.setString(1, instanceId); + ps.setString(1, theInstanceId); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } + + /** + * Clear (delete!) all scheduling data - all {@link Job}s, {@link Trigger}s + * {@link Calendar}s. + * + * @throws JobPersistenceException + */ + public void clearData(Connection conn) + throws SQLException { + + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_ALL_SIMPLE_TRIGGERS)); + ps.executeUpdate(); + ps.close(); + ps = conn.prepareStatement(rtp(DELETE_ALL_SIMPROP_TRIGGERS)); + ps.executeUpdate(); + ps.close(); + ps = conn.prepareStatement(rtp(DELETE_ALL_CRON_TRIGGERS)); + ps.executeUpdate(); + ps.close(); + ps = conn.prepareStatement(rtp(DELETE_ALL_BLOB_TRIGGERS)); + ps.executeUpdate(); + ps.close(); + ps = conn.prepareStatement(rtp(DELETE_ALL_TRIGGERS)); + ps.executeUpdate(); + ps.close(); + ps = conn.prepareStatement(rtp(DELETE_ALL_JOB_DETAILS)); + ps.executeUpdate(); + ps.close(); + ps = conn.prepareStatement(rtp(DELETE_ALL_CALENDARS)); + ps.executeUpdate(); + ps.close(); + ps = conn.prepareStatement(rtp(DELETE_ALL_PAUSED_TRIGGER_GRPS)); + ps.executeUpdate(); + } finally { + closeStatement(ps); + } + } + + //--------------------------------------------------------------------------- // jobs //--------------------------------------------------------------------------- @@ -495,7 +602,7 @@ * if there were problems serializing the JobDataMap */ public int insertJobDetail(Connection conn, JobDetail job) - throws IOException, SQLException { + throws IOException, SQLException { ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); PreparedStatement ps = null; @@ -504,32 +611,21 @@ try { ps = conn.prepareStatement(rtp(INSERT_JOB_DETAIL)); - ps.setString(1, job.getName()); - ps.setString(2, job.getGroup()); + ps.setString(1, job.getKey().getName()); + ps.setString(2, job.getKey().getGroup()); ps.setString(3, job.getDescription()); ps.setString(4, job.getJobClass().getName()); - ps.setBoolean(5, job.isDurable()); - ps.setBoolean(6, job.isVolatile()); - ps.setBoolean(7, job.isStateful()); - ps.setBoolean(8, job.requestsRecovery()); - ps.setBytes(9, baos.toByteArray()); + setBoolean(ps, 5, job.isDurable()); + setBoolean(ps, 6, job.isConcurrentExectionDisallowed()); + setBoolean(ps, 7, job.isPersistJobDataAfterExecution()); + setBoolean(ps, 8, job.requestsRecovery()); + setBytes(ps, 9, baos); insertResult = ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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; } @@ -547,7 +643,7 @@ * if there were problems serializing the JobDataMap */ public int updateJobDetail(Connection conn, JobDetail job) - throws IOException, SQLException { + throws IOException, SQLException { ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); PreparedStatement ps = null; @@ -558,32 +654,19 @@ ps = conn.prepareStatement(rtp(UPDATE_JOB_DETAIL)); ps.setString(1, job.getDescription()); ps.setString(2, job.getJobClass().getName()); - ps.setBoolean(3, job.isDurable()); - ps.setBoolean(4, job.isVolatile()); - ps.setBoolean(5, job.isStateful()); - ps.setBoolean(6, job.requestsRecovery()); - ps.setBytes(7, baos.toByteArray()); - ps.setString(8, job.getName()); - ps.setString(9, job.getGroup()); + setBoolean(ps, 3, job.isDurable()); + setBoolean(ps, 4, job.isConcurrentExectionDisallowed()); + setBoolean(ps, 5, job.isPersistJobDataAfterExecution()); + setBoolean(ps, 6, job.requestsRecovery()); + setBytes(ps, 7, baos); + ps.setString(8, job.getKey().getName()); + ps.setString(9, job.getKey().getGroup()); insertResult = ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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; } @@ -594,112 +677,55 @@ * * @param conn * the DB Connection - * @param jobName - * the name of the job - * @param groupName - * the group containing the job * @return an array of {@link * org.quartz.utils.Key} objects */ - public Key[] selectTriggerNamesForJob(Connection conn, String jobName, - String groupName) throws SQLException { + public List selectTriggerKeysForJob(Connection conn, JobKey jobKey) 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); + ps.setString(1, jobKey.getName()); + ps.setString(2, jobKey.getGroup()); rs = ps.executeQuery(); - ArrayList list = new ArrayList(10); + LinkedList list = new LinkedList(); while (rs.next()) { String trigName = rs.getString(COL_TRIGGER_NAME); String trigGroup = rs.getString(COL_TRIGGER_GROUP); - list.add(new Key(trigName, trigGroup)); + list.add(triggerKey(trigName, trigGroup)); } - Object[] oArr = list.toArray(); - Key[] kArr = new Key[oArr.length]; - System.arraycopy(oArr, 0, kArr, 0, oArr.length); - return kArr; + return list; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

* 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 { + public int deleteJobDetail(Connection conn, JobKey jobKey) + throws SQLException { PreparedStatement ps = null; try { - logger.debug("Deleting job: " + groupName + "." + jobName); + if (logger.isDebugEnabled()) { + logger.debug("Deleting job: " + jobKey); + } ps = conn.prepareStatement(rtp(DELETE_JOB_DETAIL)); - ps.setString(1, jobName); - ps.setString(2, groupName); + ps.setString(1, jobKey.getName()); + ps.setString(2, jobKey.getGroup()); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -710,37 +736,22 @@ * * @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 { + public boolean isJobNonConcurrent(Connection conn, JobKey jobKey) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { - ps = conn.prepareStatement(rtp(SELECT_JOB_STATEFUL)); - ps.setString(1, jobName); - ps.setString(2, groupName); + ps = conn.prepareStatement(rtp(SELECT_JOB_NONCONCURRENT)); + ps.setString(1, jobKey.getName()); + ps.setString(2, jobKey.getGroup()); rs = ps.executeQuery(); if (!rs.next()) { return false; } - return rs.getBoolean(COL_IS_STATEFUL); + return getBoolean(rs, COL_IS_NONCONCURRENT); } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -751,40 +762,26 @@ * * @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 { + public boolean jobExists(Connection conn, JobKey jobKey) + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_JOB_EXISTENCE)); - ps.setString(1, jobName); - ps.setString(2, groupName); + ps.setString(1, jobKey.getName()); + ps.setString(2, jobKey.getGroup()); rs = ps.executeQuery(); if (rs.next()) { return true; } else { return false; } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -801,159 +798,67 @@ * @return the number of rows updated */ public int updateJobData(Connection conn, JobDetail job) - throws IOException, SQLException { + throws IOException, SQLException { ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_JOB_DATA)); - ps.setBytes(1, baos.toByteArray()); - ps.setString(2, job.getName()); - ps.setString(3, job.getGroup()); + setBytes(ps, 1, baos); + ps.setString(2, job.getKey().getName()); + ps.setString(3, job.getKey().getGroup()); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

- * 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 of String 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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

* 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 { + public JobDetail selectJobDetail(Connection conn, JobKey jobKey, + 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); + ps.setString(1, jobKey.getName()); + ps.setString(2, jobKey.getGroup()); rs = ps.executeQuery(); - JobDetail job = null; + JobDetailImpl job = null; if (rs.next()) { - job = new JobDetail(); + job = new JobDetailImpl(); 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(rs.getBoolean(COL_IS_DURABLE)); - job.setVolatility(rs.getBoolean(COL_IS_VOLATILE)); - job.setRequestsRecovery(rs.getBoolean(COL_REQUESTS_RECOVERY)); + job.setJobClass( loadHelper.loadClass(rs.getString(COL_JOB_CLASS), Job.class)); + job.setDurability(getBoolean(rs, COL_IS_DURABLE)); + job.setRequestsRecovery(getBoolean(rs, COL_REQUESTS_RECOVERY)); - Map map = null; - if (canUseProperties()) map = getMapFromProperties(rs); - else - map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP); + Map map = null; + if (canUseProperties()) { + map = getMapFromProperties(rs); + } else { + map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP); + } if (null != map) { job.setJobDataMap(new JobDataMap(map)); @@ -962,32 +867,29 @@ return job; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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) + private Map getMapFromProperties(ResultSet rs) + throws ClassNotFoundException, IOException, SQLException { + Map map; + InputStream is = (InputStream) getJobDataFromBlob(rs, COL_JOB_DATAMAP); + if(is == null) { return null; + } Properties properties = new Properties(); - if (is != null) properties.load(is); + if (is != null) { + try { + properties.load(is); + } finally { + is.close(); + } + } map = convertFromProperty(properties); return map; } @@ -1016,18 +918,8 @@ return count; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -1040,36 +932,23 @@ * the DB Connection * @return an array of String group names */ - public String[] selectJobGroups(Connection conn) throws SQLException { + public List 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(); + LinkedList list = new LinkedList(); 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; + return list; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -1080,45 +959,70 @@ * * @param conn * the DB Connection - * @param groupName - * the group containing the jobs + * @param matcher + * the groupMatcher to evaluate the jobs against * @return an array of String job names */ - public String[] selectJobsInGroup(Connection conn, String groupName) - throws SQLException { + public Set selectJobsInGroup(Connection conn, GroupMatcher matcher) + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { - ps = conn.prepareStatement(rtp(SELECT_JOBS_IN_GROUP)); - ps.setString(1, groupName); + if(isMatcherEquals(matcher)) { + ps = conn.prepareStatement(rtp(SELECT_JOBS_IN_GROUP)); + ps.setString(1, toSqlEqualsClause(matcher)); + } + else { + ps = conn.prepareStatement(rtp(SELECT_JOBS_IN_GROUP_LIKE)); + ps.setString(1, toSqlLikeClause(matcher)); + } rs = ps.executeQuery(); - ArrayList list = new ArrayList(); + LinkedList list = new LinkedList(); while (rs.next()) { - list.add(rs.getString(1)); + list.add(jobKey(rs.getString(1), rs.getString(2))); } - Object[] oArr = list.toArray(); - String[] sArr = new String[oArr.length]; - System.arraycopy(oArr, 0, sArr, 0, oArr.length); - return sArr; + return new HashSet(list); } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } + protected boolean isMatcherEquals(final GroupMatcher matcher) { + return matcher.getCompareWithOperator().equals(StringMatcher.StringOperatorName.EQUALS); + } + + protected String toSqlEqualsClause(final GroupMatcher matcher) { + return matcher.getCompareToValue(); + } + + protected String toSqlLikeClause(final GroupMatcher matcher) { + String groupName; + switch(matcher.getCompareWithOperator()) { + case EQUALS: + groupName = matcher.getCompareToValue(); + break; + case CONTAINS: + groupName = "%" + matcher.getCompareToValue() + "%"; + break; + case ENDS_WITH: + groupName = "%" + matcher.getCompareToValue(); + break; + case STARTS_WITH: + groupName = matcher.getCompareToValue() + "%"; + break; + case ANYTHING: + groupName = "%"; + break; + default: + throw new UnsupportedOperationException("Don't know how to translate " + matcher.getCompareWithOperator() + " into SQL"); + } + return groupName; + } + //--------------------------------------------------------------------------- // triggers //--------------------------------------------------------------------------- @@ -1136,143 +1040,72 @@ * the state that the trigger should be stored in * @return the number of rows inserted */ - public int insertTrigger(Connection conn, Trigger trigger, String state, + public int insertTrigger(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { ByteArrayOutputStream baos = null; - if(trigger.getJobDataMap().size() > 0) + 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()); - ps.setBoolean(5, trigger.isVolatile()); - ps.setString(6, trigger.getDescription()); - ps.setBigDecimal(7, new BigDecimal(String.valueOf(trigger - .getNextFireTime().getTime()))); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + ps.setString(3, trigger.getJobKey().getName()); + ps.setString(4, trigger.getJobKey().getGroup()); + ps.setString(5, trigger.getDescription()); + if(trigger.getNextFireTime() != null) + ps.setBigDecimal(6, new BigDecimal(String.valueOf(trigger + .getNextFireTime().getTime()))); + else + ps.setBigDecimal(6, 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) { - ps.setString(10, TTYPE_SIMPLE); - } else if (trigger instanceof CronTrigger) { - ps.setString(10, TTYPE_CRON); - } else { // (trigger instanceof BlobTrigger) - ps.setString(10, TTYPE_BLOB); - } - ps.setBigDecimal(11, new BigDecimal(String.valueOf(trigger + ps.setBigDecimal(7, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(8, state); + + TriggerPersistenceDelegate tDel = findTriggerPersistenceDelegate(trigger); + + String type = TTYPE_BLOB; + if(tDel != null) + type = tDel.getHandledTriggerTypeDiscriminator(); + ps.setString(9, type); + + ps.setBigDecimal(10, 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()); - if(baos != null) - ps.setBytes(15, baos.toByteArray()); - else - ps.setBytes(15, null); + ps.setBigDecimal(11, new BigDecimal(String.valueOf(endTime))); + ps.setString(12, trigger.getCalendarName()); + ps.setInt(13, trigger.getMisfireInstruction()); + setBytes(ps, 14, baos); + ps.setInt(15, trigger.getPriority()); insertResult = ps.executeUpdate(); + + if(tDel == null) + insertBlobTrigger(conn, trigger); + else + tDel.insertExtendedTriggerProperties(conn, trigger, state, jobDetail); + } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

- * 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

* Insert the blob trigger data. *

* @@ -1282,8 +1115,8 @@ * the trigger to insert * @return the number of rows inserted */ - public int insertBlobTrigger(Connection conn, Trigger trigger) - throws SQLException, IOException { + public int insertBlobTrigger(Connection conn, OperableTrigger trigger) + throws SQLException, IOException { PreparedStatement ps = null; ByteArrayOutputStream os = null; @@ -1298,18 +1131,13 @@ ByteArrayInputStream is = new ByteArrayInputStream(buf); ps = conn.prepareStatement(rtp(INSERT_BLOB_TRIGGER)); - ps.setString(1, trigger.getName()); - ps.setString(2, trigger.getGroup()); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); ps.setBinaryStream(3, is, buf.length); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -1326,162 +1154,87 @@ * the state that the trigger should be stored in * @return the number of rows updated */ - public int updateTrigger(Connection conn, Trigger trigger, String state, + public int updateTrigger(Connection conn, OperableTrigger 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) + if(updateJobData && trigger.getJobDataMap().size() > 0) { baos = serializeJobData(trigger.getJobDataMap()); + } PreparedStatement ps = null; int insertResult = 0; try { - if(updateJobData) + if(updateJobData) { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER)); - else + } else { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_SKIP_DATA)); + } - ps.setString(1, trigger.getJobName()); - ps.setString(2, trigger.getJobGroup()); - ps.setBoolean(3, trigger.isVolatile()); - ps.setString(4, trigger.getDescription()); + ps.setString(1, trigger.getJobKey().getName()); + ps.setString(2, trigger.getJobKey().getGroup()); + ps.setString(3, trigger.getDescription()); long nextFireTime = -1; if (trigger.getNextFireTime() != null) { nextFireTime = trigger.getNextFireTime().getTime(); } - ps.setBigDecimal(5, new BigDecimal(String.valueOf(nextFireTime))); + ps.setBigDecimal(4, 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) { - // updateSimpleTrigger(conn, (SimpleTrigger)trigger); - ps.setString(8, TTYPE_SIMPLE); - } else if (trigger instanceof CronTrigger) { - // 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 + ps.setBigDecimal(5, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(6, state); + + TriggerPersistenceDelegate tDel = findTriggerPersistenceDelegate(trigger); + + String type = TTYPE_BLOB; + if(tDel != null) + type = tDel.getHandledTriggerTypeDiscriminator(); + + ps.setString(7, type); + + ps.setBigDecimal(8, 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.setBigDecimal(9, new BigDecimal(String.valueOf(endTime))); + ps.setString(10, trigger.getCalendarName()); + ps.setInt(11, trigger.getMisfireInstruction()); + ps.setInt(12, trigger.getPriority()); + if(updateJobData) { - ps.setBytes(13, baos.toByteArray()); - - ps.setString(14, trigger.getName()); - ps.setString(15, trigger.getGroup()); + setBytes(ps, 13, baos); + ps.setString(14, trigger.getKey().getName()); + ps.setString(15, trigger.getKey().getGroup()); + } else { + ps.setString(13, trigger.getKey().getName()); + ps.setString(14, trigger.getKey().getGroup()); } - else { - ps.setString(13, trigger.getName()); - ps.setString(14, trigger.getGroup()); - } insertResult = ps.executeUpdate(); + + if(tDel == null) + updateBlobTrigger(conn, trigger); + else + tDel.updateExtendedTriggerProperties(conn, trigger, state, jobDetail); + } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

- * 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

* Update the blob trigger data. *

* @@ -1491,8 +1244,8 @@ * the trigger to insert * @return the number of rows updated */ - public int updateBlobTrigger(Connection conn, Trigger trigger) - throws SQLException, IOException { + public int updateBlobTrigger(Connection conn, OperableTrigger trigger) + throws SQLException, IOException { PreparedStatement ps = null; ByteArrayOutputStream os = null; @@ -1508,18 +1261,15 @@ ps = conn.prepareStatement(rtp(UPDATE_BLOB_TRIGGER)); ps.setBinaryStream(1, is, buf.length); - ps.setString(2, trigger.getName()); - ps.setString(3, trigger.getGroup()); + ps.setString(2, trigger.getKey().getName()); + ps.setString(3, trigger.getKey().getGroup()); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } + closeStatement(ps); + if (os != null) { + os.close(); } - if (os != null) os.close(); } } @@ -1530,21 +1280,16 @@ * * @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 { + public boolean triggerExists(Connection conn, TriggerKey triggerKey) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGER_EXISTENCE)); - ps.setString(1, triggerName); - ps.setString(2, groupName); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); rs = ps.executeQuery(); if (rs.next()) { @@ -1553,18 +1298,8 @@ return false; } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -1575,32 +1310,22 @@ * * @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 { + public int updateTriggerState(Connection conn, TriggerKey triggerKey, + 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); - + ps.setString(2, triggerKey.getName()); + ps.setString(3, triggerKey.getGroup()); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -1612,10 +1337,6 @@ * * @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 @@ -1628,55 +1349,26 @@ * @throws SQLException */ public int updateTriggerStateFromOtherStates(Connection conn, - String triggerName, String groupName, String newState, - String oldState1, String oldState2, String oldState3) - throws SQLException { + TriggerKey triggerKey, 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(2, triggerKey.getName()); + ps.setString(3, triggerKey.getGroup()); ps.setString(4, oldState1); ps.setString(5, oldState2); ps.setString(6, oldState3); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - /** *

* Update all triggers in the given group to the given new state, if they @@ -1685,8 +1377,8 @@ * * @param conn * the DB connection - * @param groupName - * the group containing the trigger + * @param matcher + * the groupMatcher to evaluate the triggers against * @param newState * the new state for the trigger * @param oldState1 @@ -1699,27 +1391,22 @@ * @throws SQLException */ public int updateTriggerGroupStateFromOtherStates(Connection conn, - String groupName, String newState, String oldState1, + GroupMatcher matcher, 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(2, toSqlLikeClause(matcher)); ps.setString(3, oldState1); ps.setString(4, oldState2); ps.setString(5, oldState3); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -1731,10 +1418,6 @@ * * @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 @@ -1743,25 +1426,19 @@ * @throws SQLException */ public int updateTriggerStateFromOtherState(Connection conn, - String triggerName, String groupName, String newState, - String oldState) throws SQLException { + TriggerKey triggerKey, 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(2, triggerKey.getName()); + ps.setString(3, triggerKey.getGroup()); ps.setString(4, oldState); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -1773,8 +1450,8 @@ * * @param conn * the DB connection - * @param groupName - * the group containing the triggers + * @param matcher + * the groupMatcher to evaluate the triggers against * @param newState * the new state for the trigger group * @param oldState @@ -1783,25 +1460,20 @@ * @throws SQLException */ public int updateTriggerGroupStateFromOtherState(Connection conn, - String groupName, String newState, String oldState) - throws SQLException { + GroupMatcher matcher, 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(2, toSqlLikeClause(matcher)); ps.setString(3, oldState); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -1812,269 +1484,65 @@ * * @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 { + public int updateTriggerStatesForJob(Connection conn, JobKey jobKey, + 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); + ps.setString(2, jobKey.getName()); + ps.setString(3, jobKey.getGroup()); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } public int updateTriggerStatesForJobFromOtherState(Connection conn, - String jobName, String groupName, String state, String oldState) - throws SQLException { + JobKey jobKey, 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(2, jobKey.getName()); + ps.setString(3, jobKey.getGroup()); ps.setString(4, oldState); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

- * 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

- * 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 of String 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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

- * 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

* 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 { + public int deleteBlobTrigger(Connection conn, TriggerKey triggerKey) 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 { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - /** - *

- * 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); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -2085,54 +1553,51 @@ * * @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 { + public int deleteTrigger(Connection conn, TriggerKey triggerKey) throws SQLException { PreparedStatement ps = null; + deleteTriggerExtension(conn, triggerKey); + try { ps = conn.prepareStatement(rtp(DELETE_TRIGGER)); - ps.setString(1, triggerName); - ps.setString(2, groupName); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } + + protected void deleteTriggerExtension(Connection conn, TriggerKey triggerKey) throws SQLException { + for(TriggerPersistenceDelegate tDel: triggerPersistenceDelegates) { + if(tDel.deleteExtendedTriggerProperties(conn, triggerKey) > 0) + return; // as soon as one affects a row, we're done. + } + + deleteBlobTrigger(conn, triggerKey); + } + /** *

* 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 { + public int selectNumTriggersForJob(Connection conn, JobKey jobKey) 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); + ps.setString(1, jobKey.getName()); + ps.setString(2, jobKey.getGroup()); rs = ps.executeQuery(); if (rs.next()) { @@ -2141,76 +1606,72 @@ return 0; } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } /** *

* Select the job to which the trigger is associated. *

+ * + * @param conn + * the DB Connection + * @return the {@link org.quartz.JobDetail} object + * associated with the given trigger + * @throws SQLException + * @throws ClassNotFoundException + */ + public JobDetail selectJobForTrigger(Connection conn, ClassLoadHelper loadHelper, + TriggerKey triggerKey) throws ClassNotFoundException, SQLException { + return selectJobForTrigger(conn, loadHelper, triggerKey, true); + } + + /** + *

+ * Select the job to which the trigger is associated. Allow option to load actual job class or not. When case of + * remove, we do not need to load the class, which in many cases, it's no longer exists. + * + *

* * @param conn * the DB Connection - * @param triggerName - * the name of the trigger - * @param groupName - * the group containing the trigger * @return the {@link 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 { + public JobDetail selectJobForTrigger(Connection conn, ClassLoadHelper loadHelper, + TriggerKey triggerKey, boolean loadJobClass) 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); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); rs = ps.executeQuery(); if (rs.next()) { - JobDetail job = new JobDetail(); + JobDetailImpl job = new JobDetailImpl(); job.setName(rs.getString(1)); job.setGroup(rs.getString(2)); - job.setDurability(rs.getBoolean(3)); - job.setJobClass(loadHelper.loadClass(rs - .getString(4))); - job.setRequestsRecovery(rs.getBoolean(5)); + job.setDurability(getBoolean(rs, 3)); + if (loadJobClass) + job.setJobClass(loadHelper.loadClass(rs.getString(4), Job.class)); + job.setRequestsRecovery(getBoolean(rs, 5)); return job; } else { - logger.debug("No job for trigger '" + groupName + "." - + triggerName + "'."); + if (logger.isDebugEnabled()) { + logger.debug("No job for trigger '" + triggerKey + "'."); + } return null; } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -2221,57 +1682,42 @@ * * @param conn * the DB Connection - * @param jobName - * the name of the trigger - * @param groupName - * the group containing the trigger * @return an array of (@link org.quartz.Trigger) objects * associated with a given job. * @throws SQLException + * @throws JobPersistenceException */ - public Trigger[] selectTriggersForJob(Connection conn, String jobName, - String groupName) throws SQLException, ClassNotFoundException, - IOException { + public List selectTriggersForJob(Connection conn, JobKey jobKey) throws SQLException, ClassNotFoundException, + IOException, JobPersistenceException { - ArrayList trigList = new ArrayList(); + LinkedList trigList = new LinkedList(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_FOR_JOB)); - ps.setString(1, jobName); - ps.setString(2, groupName); + ps.setString(1, jobKey.getName()); + ps.setString(2, jobKey.getGroup()); rs = ps.executeQuery(); while (rs.next()) { - Trigger t = selectTrigger(conn, - rs.getString(COL_TRIGGER_NAME), - rs.getString(COL_TRIGGER_GROUP)); - if(t != null) + OperableTrigger t = selectTrigger(conn, triggerKey(rs.getString(COL_TRIGGER_NAME), rs.getString(COL_TRIGGER_GROUP))); + if(t != null) { trigList.add(t); - } - } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { } } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + } finally { + closeResultSet(rs); + closeStatement(ps); } - return (Trigger[]) trigList.toArray(new Trigger[trigList.size()]); + return trigList; } - public Trigger[] selectTriggersForCalendar(Connection conn, String calName) - throws SQLException, ClassNotFoundException, IOException { + public List selectTriggersForCalendar(Connection conn, String calName) + throws SQLException, ClassNotFoundException, IOException, JobPersistenceException { - ArrayList trigList = new ArrayList(); + LinkedList trigList = new LinkedList(); PreparedStatement ps = null; ResultSet rs = null; @@ -2281,94 +1727,42 @@ rs = ps.executeQuery(); while (rs.next()) { - trigList.add(selectTrigger(conn, - rs.getString(COL_TRIGGER_NAME), rs - .getString(COL_TRIGGER_GROUP))); + trigList.add(selectTrigger(conn, triggerKey(rs.getString(COL_TRIGGER_NAME), rs.getString(COL_TRIGGER_GROUP)))); } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } - return (Trigger[]) trigList.toArray(new Trigger[trigList.size()]); + return trigList; } - - 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); - ps.setBoolean(2, true); - rs = ps.executeQuery(); - - while (rs.next()) { - jobList.add(new Key(rs.getString(COL_JOB_NAME), rs - .getString(COL_JOB_GROUP))); - } - } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - - 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 org.quartz.Trigger} object + * @throws JobPersistenceException */ - public Trigger selectTrigger(Connection conn, String triggerName, - String groupName) throws SQLException, ClassNotFoundException, - IOException { + public OperableTrigger selectTrigger(Connection conn, TriggerKey triggerKey) throws SQLException, ClassNotFoundException, + IOException, JobPersistenceException { PreparedStatement ps = null; ResultSet rs = null; try { - Trigger trigger = null; + OperableTrigger trigger = null; ps = conn.prepareStatement(rtp(SELECT_TRIGGER)); - ps.setString(1, triggerName); - ps.setString(2, groupName); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); rs = ps.executeQuery(); if (rs.next()) { String jobName = rs.getString(COL_JOB_NAME); String jobGroup = rs.getString(COL_JOB_GROUP); - boolean volatility = rs.getBoolean(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); @@ -2377,16 +1771,20 @@ 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); + 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); @@ -2397,103 +1795,86 @@ endTimeD = new Date(endTime); } - rs.close(); - ps.close(); + if (triggerType.equals(TTYPE_BLOB)) { + rs.close(); rs = null; + ps.close(); ps = null; - if (triggerType.equals(TTYPE_SIMPLE)) { - ps = conn.prepareStatement(rtp(SELECT_SIMPLE_TRIGGER)); - ps.setString(1, triggerName); - ps.setString(2, groupName); + ps = conn.prepareStatement(rtp(SELECT_BLOB_TRIGGER)); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); 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); - if (null != map) { - st.setJobDataMap(new JobDataMap(map)); - } - trigger = st; + trigger = (OperableTrigger) getObjectFromBlob(rs, COL_BLOB); } - } else if (triggerType.equals(TTYPE_CRON)) { - ps = conn.prepareStatement(rtp(SELECT_CRON_TRIGGER)); - ps.setString(1, triggerName); - ps.setString(2, groupName); - rs = ps.executeQuery(); + } + else { + TriggerPersistenceDelegate tDel = findTriggerPersistenceDelegate(triggerType); + + if(tDel == null) + throw new JobPersistenceException("No TriggerPersistenceDelegate for trigger discriminator type: " + triggerType); - 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... + TriggerPropertyBundle triggerProps = null; + try { + triggerProps = tDel.loadExtendedTriggerProperties(conn, triggerKey); + } catch (IllegalStateException isex) { + if (isTriggerStillPresent(ps)) { + throw isex; + } else { + // QTZ-386 Trigger has been deleted + return null; } - if (null != ct) { - ct.setCalendarName(calendarName); - ct.setMisfireInstruction(misFireInstr); - ct.setVolatility(volatility); - ct.setNextFireTime(nft); - ct.setPreviousFireTime(pft); - ct.setDescription(description); - 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); + TriggerBuilder tb = newTrigger() + .withDescription(description) + .withPriority(priority) + .startAt(startTimeD) + .endAt(endTimeD) + .withIdentity(triggerKey) + .modifiedByCalendar(calendarName) + .withSchedule(triggerProps.getScheduleBuilder()) + .forJob(jobKey(jobName, jobGroup)); + + if (null != map) { + tb.usingJobData(new JobDataMap(map)); } - } else { - throw new ClassNotFoundException("class for trigger type '" - + triggerType + "' not found."); - } + + trigger = (OperableTrigger) tb.build(); + + trigger.setMisfireInstruction(misFireInstr); + trigger.setNextFireTime(nft); + trigger.setPreviousFireTime(pft); + + setTriggerStateProperties(trigger, triggerProps); + } } return trigger; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } + private boolean isTriggerStillPresent(PreparedStatement ps) throws SQLException { + ResultSet rs = null; + try { + rs = ps.executeQuery(); + return rs.next(); + } finally { + closeResultSet(rs); + } + } + + private void setTriggerStateProperties(OperableTrigger trigger, TriggerPropertyBundle props) throws JobPersistenceException { + + if(props.getStatePropertyNames() == null) + return; + + Util.setBeanProps(trigger, props.getStatePropertyNames(), props.getStatePropertyValues()); + } + /** *

* Select a trigger's JobDataMap. @@ -2516,20 +1897,19 @@ 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 map = null; + if (canUseProperties()) { map = getMapFromProperties(rs); - else - map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP); + } else { + map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP); + } rs.close(); ps.close(); @@ -2539,18 +1919,8 @@ } } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } return new JobDataMap(); @@ -2564,44 +1934,30 @@ * * @param conn * the DB Connection - * @param triggerName - * the name of the trigger - * @param groupName - * the group containing the trigger * @return the {@link org.quartz.Trigger} object */ - public String selectTriggerState(Connection conn, String triggerName, - String groupName) throws SQLException { + public String selectTriggerState(Connection conn, TriggerKey triggerKey) 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); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); rs = ps.executeQuery(); if (rs.next()) { state = rs.getString(COL_TRIGGER_STATE); - } else + } else { state = STATE_DELETED; + } return state.intern(); } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -2613,23 +1969,19 @@ * * @param conn * the DB Connection - * @param triggerName - * the name of the trigger - * @param groupName - * the group containing the trigger * @return a TriggerStatus object, or null */ public TriggerStatus selectTriggerStatus(Connection conn, - String triggerName, String groupName) throws SQLException { + TriggerKey triggerKey) 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); + ps.setString(1, triggerKey.getName()); + ps.setString(2, triggerKey.getGroup()); rs = ps.executeQuery(); if (rs.next()) { @@ -2644,24 +1996,14 @@ } status = new TriggerStatus(state, nft); - status.setKey(new Key(triggerName, groupName)); - status.setJobKey(new Key(jobName, jobGroup)); + status.setKey(triggerKey); + status.setJobKey(jobKey(jobName, jobGroup)); } return status; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -2690,18 +2032,8 @@ return count; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -2714,36 +2046,44 @@ * the DB Connection * @return an array of String group names */ - public String[] selectTriggerGroups(Connection conn) throws SQLException { + public List 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(); + LinkedList list = new LinkedList(); 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; + return list; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } + closeResultSet(rs); + closeStatement(ps); + } + } + + public List selectTriggerGroups(Connection conn, GroupMatcher matcher) throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_TRIGGER_GROUPS_FILTERED)); + ps.setString(1, toSqlLikeClause(matcher)); + rs = ps.executeQuery(); + + LinkedList list = new LinkedList(); + while (rs.next()) { + list.add(rs.getString(1)); } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + + return list; + } finally { + closeResultSet(rs); + closeStatement(ps); } } @@ -2754,47 +2094,40 @@ * * @param conn * the DB Connection - * @param groupName - * the group containing the triggers - * @return an array of String trigger names + * @param matcher + * to evaluate against known triggers + * @return a Set of TriggerKeys */ - public String[] selectTriggersInGroup(Connection conn, String groupName) - throws SQLException { + public Set selectTriggersInGroup(Connection conn, GroupMatcher matcher) + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { - ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_IN_GROUP)); - ps.setString(1, groupName); + if(isMatcherEquals(matcher)) { + ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_IN_GROUP)); + ps.setString(1, toSqlEqualsClause(matcher)); + } + else { + ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_IN_GROUP_LIKE)); + ps.setString(1, toSqlLikeClause(matcher)); + } rs = ps.executeQuery(); - ArrayList list = new ArrayList(); + Set keys = new HashSet(); while (rs.next()) { - list.add(rs.getString(1)); + keys.add(triggerKey(rs.getString(1), rs.getString(2))); } - Object[] oArr = list.toArray(); - String[] sArr = new String[oArr.length]; - System.arraycopy(oArr, 0, sArr, 0, oArr.length); - return sArr; + return keys; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } public int insertPausedTriggerGroup(Connection conn, String groupName) - throws SQLException { + throws SQLException { PreparedStatement ps = null; try { @@ -2804,17 +2137,12 @@ return rows; } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } public int deletePausedTriggerGroup(Connection conn, String groupName) - throws SQLException { + throws SQLException { PreparedStatement ps = null; try { @@ -2824,17 +2152,27 @@ return rows; } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } + public int deletePausedTriggerGroup(Connection conn, GroupMatcher matcher) + throws SQLException { + PreparedStatement ps = null; + + try { + ps = conn.prepareStatement(rtp(DELETE_PAUSED_TRIGGER_GROUP)); + ps.setString(1, toSqlLikeClause(matcher)); + int rows = ps.executeUpdate(); + + return rows; + } finally { + closeStatement(ps); + } + } + public int deleteAllPausedTriggerGroups(Connection conn) - throws SQLException { + throws SQLException { PreparedStatement ps = null; try { @@ -2843,17 +2181,12 @@ return rows; } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } public boolean isTriggerGroupPaused(Connection conn, String groupName) - throws SQLException { + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -2864,23 +2197,13 @@ return rs.next(); } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } public boolean isExistingTriggerGroup(Connection conn, String groupName) - throws SQLException { + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -2889,22 +2212,14 @@ ps.setString(1, groupName); rs = ps.executeQuery(); - if (!rs.next()) return false; + if (!rs.next()) { + return false; + } return (rs.getInt(1) > 0); } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -2936,16 +2251,11 @@ try { ps = conn.prepareStatement(rtp(INSERT_CALENDAR)); ps.setString(1, calendarName); - ps.setBytes(2, baos.toByteArray()); + setBytes(ps, 2, baos); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -2972,17 +2282,12 @@ try { ps = conn.prepareStatement(rtp(UPDATE_CALENDAR)); - ps.setBytes(1, baos.toByteArray()); + setBytes(ps, 1, baos); ps.setString(2, calendarName); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -2998,7 +2303,7 @@ * @return true if the trigger exists, false otherwise */ public boolean calendarExists(Connection conn, String calendarName) - throws SQLException { + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; @@ -3013,18 +2318,8 @@ return false; } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -3045,7 +2340,7 @@ * if there were problems deserializing the calendar */ public Calendar selectCalendar(Connection conn, String calendarName) - throws ClassNotFoundException, IOException, SQLException { + throws ClassNotFoundException, IOException, SQLException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -3064,18 +2359,8 @@ } return cal; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -3091,7 +2376,7 @@ * @return true if any triggers reference the calendar, false otherwise */ public boolean calendarIsReferenced(Connection conn, String calendarName) - throws SQLException { + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -3105,18 +2390,8 @@ return false; } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -3132,7 +2407,7 @@ * @return the number of rows deleted */ public int deleteCalendar(Connection conn, String calendarName) - throws SQLException { + throws SQLException { PreparedStatement ps = null; try { @@ -3141,12 +2416,7 @@ return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } @@ -3175,18 +2445,8 @@ return count; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -3199,36 +2459,23 @@ * the DB Connection * @return an array of String calendar names */ - public String[] selectCalendars(Connection conn) throws SQLException { + public List 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(); + LinkedList list = new LinkedList(); 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; + return list; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -3244,6 +2491,8 @@ * @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; @@ -3259,18 +2508,8 @@ return 0l; } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -3287,8 +2526,8 @@ * 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 { + public TriggerKey selectTriggerForFireTime(Connection conn, long fireTime) + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { @@ -3298,29 +2537,96 @@ rs = ps.executeQuery(); if (rs.next()) { - return new Key(rs.getString(COL_TRIGGER_NAME), rs + return new TriggerKey(rs.getString(COL_TRIGGER_NAME), rs .getString(COL_TRIGGER_GROUP)); } else { return null; } } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + 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 of getNextFireTime() 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. + * + * @deprecated - This remained for compatibility reason. Use {@link #selectTriggerToAcquire(Connection, long, long, int)} instead. + */ + public List selectTriggerToAcquire(Connection conn, long noLaterThan, long noEarlierThan) + throws SQLException { + // This old API used to always return 1 trigger. + return selectTriggerToAcquire(conn, noLaterThan, noEarlierThan, 1); + } + + /** + *

+ * 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 of getNextFireTime() of the triggers (exclusive) + * @param noEarlierThan + * highest value of getNextFireTime() of the triggers (inclusive) + * @param maxCount + * maximum number of trigger keys allow to acquired in the returning list. + * + * @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, int maxCount) + throws SQLException { + PreparedStatement ps = null; + ResultSet rs = null; + List nextTriggers = new LinkedList(); + try { + ps = conn.prepareStatement(rtp(SELECT_NEXT_TRIGGER_TO_ACQUIRE)); + + // Set max rows to retrieve + if (maxCount < 1) + maxCount = 1; // we want at least one trigger back. + ps.setMaxRows(maxCount); + + // Try to give jdbc driver a hint to hopefully not pull over more than the few rows we actually need. + // Note: in some jdbc drivers, such as MySQL, you must set maxRows before fetchSize, or you get exception! + ps.setFetchSize(maxCount); + + 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() <= maxCount) { + nextTriggers.add(triggerKey( + rs.getString(COL_TRIGGER_NAME), + rs.getString(COL_TRIGGER_GROUP))); + } + + return nextTriggers; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + *

* Insert a fired trigger. *

* @@ -3332,56 +2638,96 @@ * the state that the trigger should be stored in * @return the number of rows inserted */ - public int insertFiredTrigger(Connection conn, Trigger trigger, + public int insertFiredTrigger(Connection conn, OperableTrigger 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()); - ps.setBoolean(4, trigger.isVolatile()); - ps.setString(5, instanceId); - ps.setBigDecimal(6, new BigDecimal(String.valueOf(trigger - .getNextFireTime().getTime()))); + ps.setString(2, trigger.getKey().getName()); + ps.setString(3, trigger.getKey().getGroup()); + ps.setString(4, instanceId); + ps.setBigDecimal(5, new BigDecimal(String.valueOf(System.currentTimeMillis()))); + 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()); - ps.setBoolean(10, job.isStateful()); - ps.setBoolean(11, job.requestsRecovery()); + ps.setString(8, trigger.getJobKey().getName()); + ps.setString(9, trigger.getJobKey().getGroup()); + setBoolean(ps, 10, job.isConcurrentExectionDisallowed()); + setBoolean(ps, 11, job.requestsRecovery()); } else { ps.setString(8, null); ps.setString(9, null); - ps.setBoolean(10, false); - ps.setBoolean(11, false); + setBoolean(ps, 10, false); + setBoolean(ps, 11, false); } + ps.setInt(12, trigger.getPriority()); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } /** *

+ * Update 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 updateFiredTrigger(Connection conn, OperableTrigger trigger, + String state, JobDetail job) throws SQLException { + PreparedStatement ps = null; + try { + ps = conn.prepareStatement(rtp(UPDATE_FIRED_TRIGGER)); + + ps.setString(1, instanceId); + + ps.setBigDecimal(2, new BigDecimal(String.valueOf(System.currentTimeMillis()))); + ps.setBigDecimal(3, new BigDecimal(String.valueOf(trigger.getNextFireTime().getTime()))); + ps.setString(4, state); + + if (job != null) { + ps.setString(5, trigger.getJobKey().getName()); + ps.setString(6, trigger.getJobKey().getGroup()); + setBoolean(ps, 7, job.isConcurrentExectionDisallowed()); + setBoolean(ps, 8, job.requestsRecovery()); + } else { + ps.setString(5, null); + ps.setString(6, null); + setBoolean(ps, 7, false); + setBoolean(ps, 8, false); + } + + ps.setString(9, trigger.getFireInstanceId()); + + + 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. *

* * @return a List of FiredTriggerRecord objects. */ - public List selectFiredTriggerRecords(Connection conn, String triggerName, - String groupName) throws SQLException { + public List selectFiredTriggerRecords(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { - List lst = new LinkedList(); + List lst = new LinkedList(); if (triggerName != null) { ps = conn.prepareStatement(rtp(SELECT_FIRED_TRIGGER)); @@ -3399,34 +2745,25 @@ rec.setFireInstanceId(rs.getString(COL_ENTRY_ID)); rec.setFireInstanceState(rs.getString(COL_ENTRY_STATE)); rec.setFireTimestamp(rs.getLong(COL_FIRED_TIME)); + rec.setScheduleTimestamp(rs.getLong(COL_SCHED_TIME)); + rec.setPriority(rs.getInt(COL_PRIORITY)); rec.setSchedulerInstanceId(rs.getString(COL_INSTANCE_NAME)); - rec.setTriggerIsVolatile(rs.getBoolean(COL_IS_VOLATILE)); - rec.setTriggerKey(new Key(rs.getString(COL_TRIGGER_NAME), rs + rec.setTriggerKey(triggerKey(rs.getString(COL_TRIGGER_NAME), rs .getString(COL_TRIGGER_GROUP))); if (!rec.getFireInstanceState().equals(STATE_ACQUIRED)) { - rec.setJobIsStateful(rs.getBoolean(COL_IS_STATEFUL)); + rec.setJobDisallowsConcurrentExecution(getBoolean(rs, COL_IS_NONCONCURRENT)); rec.setJobRequestsRecovery(rs .getBoolean(COL_REQUESTS_RECOVERY)); - rec.setJobKey(new Key(rs.getString(COL_JOB_NAME), rs + rec.setJobKey(jobKey(rs.getString(COL_JOB_NAME), rs .getString(COL_JOB_GROUP))); } lst.add(rec); } return lst; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -3438,12 +2775,11 @@ * * @return a List of FiredTriggerRecord objects. */ - public List selectFiredTriggerRecordsByJob(Connection conn, String jobName, - String groupName) throws SQLException { + public List selectFiredTriggerRecordsByJob(Connection conn, String jobName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { - List lst = new LinkedList(); + List lst = new LinkedList(); if (jobName != null) { ps = conn.prepareStatement(rtp(SELECT_FIRED_TRIGGERS_OF_JOB)); @@ -3462,44 +2798,35 @@ rec.setFireInstanceId(rs.getString(COL_ENTRY_ID)); rec.setFireInstanceState(rs.getString(COL_ENTRY_STATE)); rec.setFireTimestamp(rs.getLong(COL_FIRED_TIME)); + rec.setScheduleTimestamp(rs.getLong(COL_SCHED_TIME)); + rec.setPriority(rs.getInt(COL_PRIORITY)); rec.setSchedulerInstanceId(rs.getString(COL_INSTANCE_NAME)); - rec.setTriggerIsVolatile(rs.getBoolean(COL_IS_VOLATILE)); - rec.setTriggerKey(new Key(rs.getString(COL_TRIGGER_NAME), rs + rec.setTriggerKey(triggerKey(rs.getString(COL_TRIGGER_NAME), rs .getString(COL_TRIGGER_GROUP))); if (!rec.getFireInstanceState().equals(STATE_ACQUIRED)) { - rec.setJobIsStateful(rs.getBoolean(COL_IS_STATEFUL)); + rec.setJobDisallowsConcurrentExecution(getBoolean(rs, COL_IS_NONCONCURRENT)); rec.setJobRequestsRecovery(rs .getBoolean(COL_REQUESTS_RECOVERY)); - rec.setJobKey(new Key(rs.getString(COL_JOB_NAME), rs + rec.setJobKey(jobKey(rs.getString(COL_JOB_NAME), rs .getString(COL_JOB_GROUP))); } lst.add(rec); } return lst; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } - public List selectInstancesFiredTriggerRecords(Connection conn, + public List selectInstancesFiredTriggerRecords(Connection conn, String instanceName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { - List lst = new LinkedList(); + List lst = new LinkedList(); ps = conn.prepareStatement(rtp(SELECT_INSTANCES_FIRED_TRIGGERS)); ps.setString(1, instanceName); @@ -3511,39 +2838,63 @@ rec.setFireInstanceId(rs.getString(COL_ENTRY_ID)); rec.setFireInstanceState(rs.getString(COL_ENTRY_STATE)); rec.setFireTimestamp(rs.getLong(COL_FIRED_TIME)); + rec.setScheduleTimestamp(rs.getLong(COL_SCHED_TIME)); rec.setSchedulerInstanceId(rs.getString(COL_INSTANCE_NAME)); - rec.setTriggerIsVolatile(rs.getBoolean(COL_IS_VOLATILE)); - rec.setTriggerKey(new Key(rs.getString(COL_TRIGGER_NAME), rs + rec.setTriggerKey(triggerKey(rs.getString(COL_TRIGGER_NAME), rs .getString(COL_TRIGGER_GROUP))); if (!rec.getFireInstanceState().equals(STATE_ACQUIRED)) { - rec.setJobIsStateful(rs.getBoolean(COL_IS_STATEFUL)); + rec.setJobDisallowsConcurrentExecution(getBoolean(rs, COL_IS_NONCONCURRENT)); rec.setJobRequestsRecovery(rs .getBoolean(COL_REQUESTS_RECOVERY)); - rec.setJobKey(new Key(rs.getString(COL_JOB_NAME), rs + rec.setJobKey(jobKey(rs.getString(COL_JOB_NAME), rs .getString(COL_JOB_GROUP))); } + rec.setPriority(rs.getInt(COL_PRIORITY)); lst.add(rec); } return lst; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } /** *

+ * 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. *

* @@ -3554,142 +2905,89 @@ * @return the number of rows deleted */ public int deleteFiredTrigger(Connection conn, String entryId) - throws SQLException { + throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_FIRED_TRIGGER)); ps.setString(1, entryId); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } - public int selectJobExecutionCount(Connection conn, String jobName, - String jobGroup) throws SQLException { + public int selectJobExecutionCount(Connection conn, JobKey jobKey) 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); + ps.setString(1, jobKey.getName()); + ps.setString(2, jobKey.getGroup()); rs = ps.executeQuery(); - if (rs.next()) return rs.getInt(1); - else - return 0; - + return (rs.next()) ? rs.getInt(1) : 0; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } - - public int deleteVolatileFiredTriggers(Connection conn) throws SQLException { + + public int insertSchedulerState(Connection conn, String theInstanceId, + long checkInTime, long interval) + throws SQLException { PreparedStatement ps = null; try { - ps = conn.prepareStatement(rtp(DELETE_VOLATILE_FIRED_TRIGGERS)); - ps.setBoolean(1, true); - - return ps.executeUpdate(); - } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - - public int insertSchedulerState(Connection conn, String instanceId, - long checkInTime, long interval, String recoverer) - throws SQLException { - PreparedStatement ps = null; - try { ps = conn.prepareStatement(rtp(INSERT_SCHEDULER_STATE)); - ps.setString(1, instanceId); + ps.setString(1, theInstanceId); ps.setLong(2, checkInTime); ps.setLong(3, interval); - ps.setString(4, recoverer); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } - public int deleteSchedulerState(Connection conn, String instanceId) - throws SQLException { + public int deleteSchedulerState(Connection conn, String theInstanceId) + throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_SCHEDULER_STATE)); - ps.setString(1, instanceId); + ps.setString(1, theInstanceId); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } - public int updateSchedulerState(Connection conn, String instanceId, long checkInTime, String recoverer) - throws SQLException { + public int updateSchedulerState(Connection conn, String theInstanceId, long checkInTime) + throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_SCHEDULER_STATE)); ps.setLong(1, checkInTime); - ps.setString(2, recoverer); - ps.setString(3, instanceId); + ps.setString(2, theInstanceId); return ps.executeUpdate(); } finally { - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeStatement(ps); } } - public List selectSchedulerStateRecords(Connection conn, String instanceId) - throws SQLException { + public List selectSchedulerStateRecords(Connection conn, String theInstanceId) + throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { - List lst = new LinkedList(); + List lst = new LinkedList(); - if (instanceId != null) { + if (theInstanceId != null) { ps = conn.prepareStatement(rtp(SELECT_SCHEDULER_STATE)); - ps.setString(1, instanceId); + ps.setString(1, theInstanceId); } else { ps = conn.prepareStatement(rtp(SELECT_SCHEDULER_STATES)); } @@ -3701,25 +2999,14 @@ rec.setSchedulerInstanceId(rs.getString(COL_INSTANCE_NAME)); rec.setCheckinTimestamp(rs.getLong(COL_LAST_CHECKIN_TIME)); rec.setCheckinInterval(rs.getLong(COL_CHECKIN_INTERVAL)); - rec.setRecoverer(rs.getString(COL_RECOVERER)); lst.add(rec); } return lst; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } + closeResultSet(rs); + closeStatement(ps); } } @@ -3739,9 +3026,16 @@ * @return the query, with proper table prefix substituted */ protected final String rtp(String query) { - return Util.rtp(query, tablePrefix); + return Util.rtp(query, tablePrefix, getSchedulerNameLiteral()); } + private String schedNameLiteral = null; + protected String getSchedulerNameLiteral() { + if(schedNameLiteral == null) + schedNameLiteral = "'" + schedName + "'"; + return schedNameLiteral; + } + /** *

* Create a serialized java.util.ByteArrayOutputStream @@ -3755,7 +3049,7 @@ * if serialization causes an error */ protected ByteArrayOutputStream serializeObject(Object obj) - throws IOException { + throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (null != obj) { ObjectOutputStream out = new ObjectOutputStream(baos); @@ -3771,29 +3065,61 @@ * version of a {@link org.quartz.JobDataMap}. *

* - * @param obj - * the object to serialize + * @param data + * the JobDataMap to serialize * @return the serialized ByteArrayOutputStream * @throws IOException * if serialization causes an error */ protected ByteArrayOutputStream serializeJobData(JobDataMap data) - throws IOException { - if (canUseProperties()) return serializeProperties(data); + throws IOException { + if (canUseProperties()) { + return serializeProperties(data); + } - if (null != data) { - data.removeTransientData(); + try { return serializeObject(data); - } else { - return serializeObject(null); + } catch (NotSerializableException e) { + throw new NotSerializableException( + "Unable to serialize JobDataMap for insertion into " + + "database because the value of property '" + + getKeyOfNonSerializableValue(data) + + "' is not serializable: " + e.getMessage()); } } /** + * Find the key of the first non-serializable value in the given Map. + * + * @return The key of the first non-serializable value in the given Map or + * null if all values are serializable. + */ + protected Object getKeyOfNonSerializableValue(Map data) { + for (Iterator entryIter = data.entrySet().iterator(); entryIter.hasNext();) { + Map.Entry entry = (Map.Entry)entryIter.next(); + + ByteArrayOutputStream baos = null; + try { + baos = serializeObject(entry.getValue()); + } catch (IOException e) { + return entry.getKey(); + } finally { + if (baos != null) { + try { baos.close(); } catch (IOException ignore) {} + } + } + } + + // As long as it is true that the Map was not serializable, we should + // not hit this case. + return null; + } + + /** * serialize the java.util.Properties */ private ByteArrayOutputStream serializeProperties(JobDataMap data) - throws IOException { + throws IOException { ByteArrayOutputStream ba = new ByteArrayOutputStream(); if (null != data) { Properties properties = convertToProperty(data.getWrappedMap()); @@ -3806,40 +3132,37 @@ /** * convert the JobDataMap into a list of properties */ - protected Map convertFromProperty(Properties properties) throws IOException { - Map data = new HashMap(); - Set keys = properties.keySet(); - Iterator it = keys.iterator(); - while (it.hasNext()) { - Object key = it.next(); - Object val = properties.get(key); - data.put(key, val); - } - - return data; + protected Map convertFromProperty(Properties properties) throws IOException { + return new HashMap(properties); } /** * convert the JobDataMap into a list of properties */ - protected Properties convertToProperty(Map data) throws IOException { + protected Properties convertToProperty(Map data) throws IOException { Properties properties = new Properties(); - Set keys = data.keySet(); - Iterator it = keys.iterator(); - while (it.hasNext()) { - Object key = it.next(); - Object val = data.get(key); - if(!(key instanceof String)) + + for (Iterator entryIter = data.entrySet().iterator(); entryIter.hasNext();) { + Map.Entry entry = (Map.Entry)entryIter.next(); + + Object key = entry.getKey(); + Object val = (entry.getValue() == null) ? "" : entry.getValue(); + + if(!(key instanceof String)) { throw new IOException("JobDataMap keys/values must be Strings " + "when the 'useProperties' property is set. " + " offending Key: " + key); - if(!(val instanceof String)) + } + + if(!(val instanceof String)) { throw new IOException("JobDataMap values must be Strings " + "when the 'useProperties' property is set. " + " Key of offending value: " + key); - if (val == null) val = ""; + } + properties.put(key, val); } + return properties; } @@ -3861,90 +3184,29 @@ * if deserialization causes an error */ protected Object getObjectFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + throws ClassNotFoundException, IOException, SQLException { Object obj = null; Blob blobLocator = rs.getBlob(colName); - if (blobLocator != null) { + if (blobLocator != null && blobLocator.length() != 0) { InputStream binaryInput = blobLocator.getBinaryStream(); if (null != binaryInput) { - ObjectInputStream in = new ObjectInputStream(binaryInput); - obj = in.readObject(); - in.close(); - } - } - return obj; - } - - public Key[] selectVolatileTriggers(Connection conn) throws SQLException { - PreparedStatement ps = null; - ResultSet rs = null; - - try { - ps = conn.prepareStatement(rtp(SELECT_VOLATILE_TRIGGERS)); - ps.setBoolean(1, true); - 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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { + if (binaryInput instanceof ByteArrayInputStream + && ((ByteArrayInputStream) binaryInput).available() == 0 ) { + //do nothing + } else { + ObjectInputStream in = new ObjectInputStream(binaryInput); + try { + obj = in.readObject(); + } finally { + in.close(); + } } } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } - } - } - public Key[] selectVolatileJobs(Connection conn) throws SQLException { - PreparedStatement ps = null; - ResultSet rs = null; - - try { - ps = conn.prepareStatement(rtp(SELECT_VOLATILE_JOBS)); - ps.setBoolean(1, true); - rs = ps.executeQuery(); - - ArrayList list = new ArrayList(); - while (rs.next()) { - String triggerName = rs.getString(COL_JOB_NAME); - String groupName = rs.getString(COL_JOB_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 { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } - } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } - } } + return obj; } /** @@ -3964,8 +3226,8 @@ * @throws IOException * if deserialization causes an error */ - protected Object getJobDetailFromBlob(ResultSet rs, String colName) - throws ClassNotFoundException, IOException, SQLException { + protected Object getJobDataFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { if (canUseProperties()) { Blob blobLocator = rs.getBlob(colName); if (blobLocator != null) { @@ -3982,11 +3244,11 @@ /** * @see org.quartz.impl.jdbcjobstore.DriverDelegate#selectPausedTriggerGroups(java.sql.Connection) */ - public Set selectPausedTriggerGroups(Connection conn) throws SQLException { + public Set selectPausedTriggerGroups(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; - HashSet set = new HashSet(); + HashSet set = new HashSet(); try { ps = conn.prepareStatement(rtp(SELECT_PAUSED_TRIGGER_GROUPS)); rs = ps.executeQuery(); @@ -3997,20 +3259,81 @@ } return set; } finally { - if (null != rs) { - try { - rs.close(); - } catch (SQLException ignore) { - } + closeResultSet(rs); + closeStatement(ps); + } + } + + /** + * Cleanup helper method that closes the given ResultSet + * while ignoring any errors. + */ + protected static void closeResultSet(ResultSet rs) { + if (null != rs) { + try { + rs.close(); + } catch (SQLException ignore) { } - if (null != ps) { - try { - ps.close(); - } catch (SQLException ignore) { - } + } + } + + /** + * Cleanup helper method that closes the given Statement + * while ignoring any errors. + */ + protected static 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 Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/StdRowLockSemaphore.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/StdRowLockSemaphore.java (.../StdRowLockSemaphore.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/StdRowLockSemaphore.java (.../StdRowLockSemaphore.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,28 +15,21 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.impl.jdbcjobstore; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.HashSet; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - /** - * An interface for providing thread/resource locking in order to protect - * resources from being altered by multiple threads at the same time. + * 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 implements Semaphore, Constants, - StdJDBCConstants { +public class StdRowLockSemaphore extends DBSemaphore { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -47,24 +40,13 @@ */ public static final String SELECT_FOR_LOCK = "SELECT * FROM " - + TABLE_PREFIX_SUBST + TABLE_LOCKS + " WHERE " + COL_LOCK_NAME - + " = ? FOR UPDATE"; + + TABLE_PREFIX_SUBST + TABLE_LOCKS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_LOCK_NAME + " = ? FOR UPDATE"; - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Data members. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ + public static final String INSERT_LOCK = "INSERT INTO " + + TABLE_PREFIX_SUBST + TABLE_LOCKS + "(" + COL_SCHEDULER_NAME + ", " + COL_LOCK_NAME + ") VALUES (" + + SCHED_NAME_SUBST + ", ?)"; - ThreadLocal lockOwners = new ThreadLocal(); - - // java.util.HashMap threadLocksOb = new java.util.HashMap(); - private String selectWithLockSQL = SELECT_FOR_LOCK; - - private String tablePrefix; - /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -73,15 +55,14 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - public StdRowLockSemaphore(String tablePrefix, String selectWithLockSQL) { - this.tablePrefix = tablePrefix; - - if (selectWithLockSQL != null && selectWithLockSQL.trim().length() != 0) - this.selectWithLockSQL = selectWithLockSQL; - - this.selectWithLockSQL = Util.rtp(this.selectWithLockSQL, tablePrefix); + public StdRowLockSemaphore() { + super(DEFAULT_TABLE_PREFIX, null, SELECT_FOR_LOCK, INSERT_LOCK); } + public StdRowLockSemaphore(String tablePrefix, String schedName, String selectWithLockSQL) { + super(tablePrefix, schedName, selectWithLockSQL != null ? selectWithLockSQL : SELECT_FOR_LOCK, INSERT_LOCK); + } + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -90,130 +71,115 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - Log getLog() { - return LogFactory.getLog(getClass()); - //return LogFactory.getLog("LOCK:"+Thread.currentThread().getName()); - } - - 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. + * Execute the SQL select for update that will lock the proper database row. */ - public boolean obtainLock(Connection conn, String lockName) - throws LockException { - - lockName = lockName.intern(); - - Log log = getLog(); + @Override + protected void executeSQL(Connection conn, final String lockName, final String expandedSQL, final String expandedInsertSQL) throws LockException { + PreparedStatement ps = null; + ResultSet rs = null; + SQLException initCause = null; - if(log.isDebugEnabled()) - log.debug( - "Lock '" + lockName + "' is desired by: " - + Thread.currentThread().getName()); - if (!isLockOwner(conn, lockName)) { - - PreparedStatement ps = null; - ResultSet rs = null; + // attempt lock two times (to work-around possible race conditions in inserting the lock row the first time running) + int count = 0; + do { + count++; try { - ps = conn.prepareStatement(selectWithLockSQL); + ps = conn.prepareStatement(expandedSQL); ps.setString(1, lockName); - - if(log.isDebugEnabled()) - log.debug( - "Lock '" + lockName + "' is being obtained: " - + Thread.currentThread().getName()); + if (getLog().isDebugEnabled()) { + getLog().debug( + "Lock '" + lockName + "' is being obtained: " + + Thread.currentThread().getName()); + } rs = ps.executeQuery(); - if (!rs.next()) + if (!rs.next()) { + getLog().debug( + "Inserting new lock row for lock: '" + lockName + "' being obtained by thread: " + + Thread.currentThread().getName()); + rs.close(); + rs = null; + ps.close(); + ps = null; + ps = conn.prepareStatement(expandedInsertSQL); + ps.setString(1, lockName); + + int res = ps.executeUpdate(); + + if(res != 1) { + if(count < 3) { + // pause a bit to give another thread some time to commit the insert of the new lock row + try { + Thread.sleep(1000L); + } catch (InterruptedException ignore) { + Thread.currentThread().interrupt(); + } + // try again ... + continue; + } + throw new SQLException(Util.rtp( - "No row exists in table " + TABLE_PREFIX_SUBST - + TABLE_LOCKS + " for lock named: " - + lockName, tablePrefix)); + "No row exists, and one could not be inserted in table " + TABLE_PREFIX_SUBST + TABLE_LOCKS + + " for lock named: " + lockName, getTablePrefix(), getSchedulerNameLiteral())); + } + } + + return; // obtained lock, go } catch (SQLException sqle) { //Exception src = // (Exception)getThreadLocksObtainer().get(lockName); //if(src != null) // src.printStackTrace(); //else // System.err.println("--- ***************** NO OBTAINER!"); - - if(log.isDebugEnabled()) - log.debug( - "Lock '" + lockName + "' was not obtained by: " - + Thread.currentThread().getName()); + + if(initCause == null) + initCause = sqle; + + if (getLog().isDebugEnabled()) { + getLog().debug( + "Lock '" + lockName + "' was not obtained by: " + + Thread.currentThread().getName() + (count < 3 ? " - will try again." : "")); + } + + if(count < 3) { + // pause a bit to give another thread some time to commit the insert of the new lock row + try { + Thread.sleep(1000L); + } catch (InterruptedException ignore) { + Thread.currentThread().interrupt(); + } + // try again ... + continue; + } + throw new LockException("Failure obtaining db row lock: " + sqle.getMessage(), sqle); } finally { - if (rs != null) try { - rs.close(); - } catch (Exception ignore) { + if (rs != null) { + try { + rs.close(); + } catch (Exception ignore) { + } } - if (ps != null) try { - ps.close(); - } catch (Exception ignore) { + if (ps != null) { + try { + ps.close(); + } catch (Exception ignore) { + } } } - 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; + } while(count < 4); + + throw new LockException("Failure obtaining db row lock, reached maximum number of attempts. Initial exception (if any) attached as root cause.", initCause); } - /** - * 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 retun by: " - + Thread.currentThread().getName() - + " -- but not owner!", - new Exception("stack-trace of wrongful returner")); - + protected String getSelectWithLockSQL() { + return getSQL(); } - /** - * 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); + public void setSelectWithLockSQL(String selectWithLockSQL) { + setSQL(selectWithLockSQL); } - } Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SybaseDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SybaseDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/SybaseDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,108 @@ +/* + * Copyright 2010 Terracotta, Inc. + * + * 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 org.quartz.impl.jdbcjobstore; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.quartz.spi.ClassLoadHelper; +import org.slf4j.Logger; + +/** + *

+ * This is a driver delegate for the Sybase database. + *

+ * + * @author Jeffrey Wescott + * @author jhouse + * @author Ray Case + */ +public class SybaseDelegate extends StdJDBCDelegate { + + //--------------------------------------------------------------------------- + // 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. + *

+ * + * @param rs + * the result set, already queued to the correct row + * @param colName + * the column name for the BLOB + * @return the deserialized Object from the ResultSet BLOB + * @throws ClassNotFoundException + * if a class found during deserialization cannot be found + * @throws IOException + * if deserialization causes an error + */ + @Override + protected Object getObjectFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { + InputStream binaryInput = rs.getBinaryStream(colName); + + if(binaryInput == null || binaryInput.available() == 0) { + return null; + } + + Object obj = null; + + ObjectInputStream in = new ObjectInputStream(binaryInput); + try { + obj = in.readObject(); + } finally { + in.close(); + } + + return obj; + } + + @Override + protected Object getJobDataFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { + if (canUseProperties()) { + InputStream binaryInput = rs.getBinaryStream(colName); + return binaryInput; + } + return getObjectFromBlob(rs, colName); + } + + /** + * 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. + */ + @Override + protected void setBytes(PreparedStatement ps, int index, ByteArrayOutputStream baos) throws SQLException { + ps.setBytes(index, (baos == null) ? null: baos.toByteArray()); + } +} + +// EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/TablePrefixAware.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/TablePrefixAware.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/TablePrefixAware.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,25 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 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); + void setSchedName(String schedName); +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/TriggerPersistenceDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/TriggerPersistenceDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/TriggerPersistenceDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,59 @@ +package org.quartz.impl.jdbcjobstore; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; + +import org.quartz.JobDetail; +import org.quartz.ScheduleBuilder; +import org.quartz.TriggerKey; +import org.quartz.spi.OperableTrigger; + +/** + * An interface which provides an implementation for storing a particular + * type of Trigger's extended properties. + * + * @author jhouse + */ +public interface TriggerPersistenceDelegate { + + public void initialize(String tablePrefix, String schedulerName); + + public boolean canHandleTriggerType(OperableTrigger trigger); + + public String getHandledTriggerTypeDiscriminator(); + + public int insertExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException; + + public int updateExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException; + + public int deleteExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException; + + public TriggerPropertyBundle loadExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException; + + + class TriggerPropertyBundle { + + private ScheduleBuilder sb; + private String[] statePropertyNames; + private Object[] statePropertyValues; + + public TriggerPropertyBundle(ScheduleBuilder sb, String[] statePropertyNames, Object[] statePropertyValues) { + this.sb = sb; + this.statePropertyNames = statePropertyNames; + this.statePropertyValues = statePropertyValues; + } + + public ScheduleBuilder getScheduleBuilder() { + return sb; + } + + public String[] getStatePropertyNames() { + return statePropertyNames; + } + + public Object[] getStatePropertyValues() { + return statePropertyValues; + } + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/TriggerStatus.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/TriggerStatus.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/TriggerStatus.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,131 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.jdbcjobstore; + +import java.util.Date; + +import org.quartz.JobKey; +import org.quartz.TriggerKey; + + +/** + *

+ * Object representing a job or trigger key. + *

+ * + * @author James House + */ +public class TriggerStatus { + + // FUTURE_TODO: Repackage under spi or root pkg ?, put status constants here. + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private TriggerKey key; + + private JobKey jobKey; + + private String status; + + private Date nextFireTime; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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) { + this.status = status; + this.nextFireTime = nextFireTime; + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public JobKey getJobKey() { + return jobKey; + } + + public void setJobKey(JobKey jobKey) { + this.jobKey = jobKey; + } + + public TriggerKey getKey() { + return key; + } + + public void setKey(TriggerKey key) { + this.key = key; + } + + /** + *

+ * Get the name portion of the key. + *

+ * + * @return the name + */ + public String getStatus() { + return status; + } + + /** + *

+ * Get the group portion of the key. + *

+ * + * @return the group + */ + public Date getNextFireTime() { + return nextFireTime; + } + + /** + *

+ * Return the string representation of the TriggerStatus. + *

+ * + */ + @Override + public String toString() { + return "status: " + getStatus() + ", next Fire = " + getNextFireTime(); + } +} + +// EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/UpdateLockRowSemaphore.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/UpdateLockRowSemaphore.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/UpdateLockRowSemaphore.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,139 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 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_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_LOCK_NAME + " = ? "; + + + public static final String INSERT_LOCK = "INSERT INTO " + + TABLE_PREFIX_SUBST + TABLE_LOCKS + "(" + COL_SCHEDULER_NAME + ", " + COL_LOCK_NAME + ") VALUES (" + + SCHED_NAME_SUBST + ", ?)"; + + private static final int RETRY_COUNT = 2; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public UpdateLockRowSemaphore() { + super(DEFAULT_TABLE_PREFIX, null, UPDATE_FOR_LOCK, INSERT_LOCK); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Execute the SQL select for update that will lock the proper database row. + */ + @Override + protected void executeSQL(Connection conn, final String lockName, final String expandedSQL, final String expandedInsertSQL) throws LockException { + SQLException lastFailure = null; + for (int i = 0; i < RETRY_COUNT; i++) { + try { + if (!lockViaUpdate(conn, lockName, expandedSQL)) { + lockViaInsert(conn, lockName, expandedInsertSQL); + } + return; + } catch (SQLException e) { + lastFailure = e; + if ((i + 1) == RETRY_COUNT) { + getLog().debug("Lock '{}' was not obtained by: {}", lockName, Thread.currentThread().getName()); + } else { + getLog().debug("Lock '{}' was not obtained by: {} - will try again.", lockName, Thread.currentThread().getName()); + } + try { + Thread.sleep(1000L); + } catch (InterruptedException _) { + Thread.currentThread().interrupt(); + } + } + } + throw new LockException("Failure obtaining db row lock: " + lastFailure.getMessage(), lastFailure); + } + + protected String getUpdateLockRowSQL() { + return getSQL(); + } + + public void setUpdateLockRowSQL(String updateLockRowSQL) { + setSQL(updateLockRowSQL); + } + + private boolean lockViaUpdate(Connection conn, String lockName, String sql) throws SQLException { + PreparedStatement ps = conn.prepareStatement(sql); + try { + ps.setString(1, lockName); + getLog().debug("Lock '" + lockName + "' is being obtained: " + Thread.currentThread().getName()); + return ps.executeUpdate() >= 1; + } finally { + ps.close(); + } + } + + private void lockViaInsert(Connection conn, String lockName, String sql) throws SQLException { + getLog().debug("Inserting new lock row for lock: '" + lockName + "' being obtained by thread: " + Thread.currentThread().getName()); + PreparedStatement ps = conn.prepareStatement(sql); + try { + ps.setString(1, lockName); + if(ps.executeUpdate() != 1) { + throw new SQLException(Util.rtp( + "No row exists, and one could not be inserted in table " + TABLE_PREFIX_SUBST + TABLE_LOCKS + + " for lock named: " + lockName, getTablePrefix(), getSchedulerNameLiteral())); + } + } finally { + ps.close(); + } + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/Util.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/Util.java (.../Util.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/Util.java (.../Util.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,13 +15,20 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ + package org.quartz.impl.jdbcjobstore; +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.text.MessageFormat; +import java.util.Locale; +import org.quartz.JobPersistenceException; + /** *

* This class contains utility functions for use in all delegate classes. @@ -31,6 +38,12 @@ */ public final class Util { + /** + * Private constructor because this is a pure utility class. + */ + private Util() { + } + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -47,12 +60,12 @@ * * @param query * the unsubstitued query - * @param query + * @param tablePrefix * the table prefix * @return the query, with proper table prefix substituted */ - public static final String rtp(String query, String tablePrefix) { - return MessageFormat.format(query, new Object[]{tablePrefix}); + public static String rtp(String query, String tablePrefix, String schedNameLiteral) { + return MessageFormat.format(query, new Object[]{tablePrefix, schedNameLiteral}); } /** @@ -66,7 +79,7 @@ * the group containing the job * @return a unique String key */ - static final String getJobNameKey(String jobName, String groupName) { + static String getJobNameKey(String jobName, String groupName) { return (groupName + "_$x$x$_" + jobName).intern(); } @@ -81,9 +94,90 @@ * the group containing the trigger * @return a unique String key */ - static final String getTriggerNameKey(String triggerName, String groupName) { + static String getTriggerNameKey(String triggerName, String groupName) { return (groupName + "_$x$x$_" + triggerName).intern(); } + + /** + * Cleanup helper method that closes the given ResultSet + * while ignoring any errors. + */ + public static 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. + */ + public static void closeStatement(Statement statement) { + if (null != statement) { + try { + statement.close(); + } catch (SQLException ignore) { + } + } + } + + + public static void setBeanProps(Object obj, String[] propNames, Object[] propValues) throws JobPersistenceException { + + if(propNames == null || propNames.length == 0) + return; + if(propNames.length != propValues.length) + throw new IllegalArgumentException("propNames[].lenght != propValues[].length"); + + String name = null; + + try { + BeanInfo bi = Introspector.getBeanInfo(obj.getClass()); + PropertyDescriptor[] propDescs = bi.getPropertyDescriptors(); + + for(int i=0; i < propNames.length; i++) { + name = propNames[i]; + String c = name.substring(0, 1).toUpperCase(Locale.US); + String methName = "set" + c + name.substring(1); + + java.lang.reflect.Method setMeth = getSetMethod(methName, propDescs); + + if (setMeth == null) { + throw new NoSuchMethodException( + "No setter for property '" + name + "'"); + } + + Class[] params = setMeth.getParameterTypes(); + if (params.length != 1) { + throw new NoSuchMethodException( + "No 1-argument setter for property '" + name + "'"); + } + + setMeth.invoke(obj, new Object[]{ propValues[i] }); + } + } + catch(Exception e) { + throw new JobPersistenceException( + "Unable to set property named: " + name +" of object of type: " + obj.getClass().getCanonicalName(), + e); + } + } + + private static 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 && wMeth.getName().equals(name)) { + return wMeth; + } + } + + return null; + } + } // EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/WebLogicDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/WebLogicDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/WebLogicDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,108 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. Inc. + * + * 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 org.quartz.impl.jdbcjobstore; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.sql.Blob; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.quartz.spi.ClassLoadHelper; +import org.slf4j.Logger; + +/** + *

+ * This is a driver delegate for the WebLogic JDBC driver. + *

+ * + * @see org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate + * @author Jeffrey Wescott + */ +public class WebLogicDelegate extends StdJDBCDelegate { + + //--------------------------------------------------------------------------- + // 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. + *

+ * + * @param rs + * the result set, already queued to the correct row + * @param colName + * the column name for the BLOB + * @return the deserialized Object from the ResultSet BLOB + * @throws ClassNotFoundException + * if a class found during deserialization cannot be found + * @throws IOException + * if deserialization causes an error + */ + @Override + protected Object getObjectFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { + + Object obj = null; + + Blob blobLocator = rs.getBlob(colName); + InputStream binaryInput = null; + try { + if (null != blobLocator && blobLocator.length() > 0) { + binaryInput = blobLocator.getBinaryStream(); + } + } catch (Exception ignore) { + } + + if (null != binaryInput) { + ObjectInputStream in = new ObjectInputStream(binaryInput); + try { + obj = in.readObject(); + } finally { + in.close(); + } + } + + return obj; + } + + @Override + protected Object getJobDataFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { + + if (canUseProperties()) { + Blob blobLocator = rs.getBlob(colName); + InputStream binaryInput = null; + try { + if (null != blobLocator && blobLocator.length() > 0) { + binaryInput = blobLocator.getBinaryStream(); + } + } catch (Exception ignore) { + } + return binaryInput; + } + + return getObjectFromBlob(rs, colName); + } +} + +// EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/oracle/OracleDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/oracle/OracleDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/oracle/OracleDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,604 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. Inc. + * + * 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 org.quartz.impl.jdbcjobstore.oracle; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.math.BigDecimal; +import java.sql.Blob; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.quartz.Calendar; +import org.quartz.JobDetail; +import org.quartz.impl.jdbcjobstore.StdJDBCDelegate; +import org.quartz.impl.jdbcjobstore.TriggerPersistenceDelegate; +import org.quartz.spi.ClassLoadHelper; +import org.quartz.spi.OperableTrigger; +import org.slf4j.Logger; + +/** + *

+ * This is a driver delegate for the Oracle 10 and 11 database. + *

+ * + * @see org.quartz.impl.jdbcjobstore.WebLogicDelegate + * @see org.quartz.impl.jdbcjobstore.oracle.weblogic.WebLogicOracleDelegate + * @author James House + * @author Patrick Lightbody + * @author Eric Mueller + */ +public class OracleDelegate extends StdJDBCDelegate { + + public static final String INSERT_ORACLE_JOB_DETAIL = "INSERT INTO " + + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " (" + COL_SCHEDULER_NAME + ", " + + COL_JOB_NAME + ", " + COL_JOB_GROUP + ", " + COL_DESCRIPTION + ", " + + COL_JOB_CLASS + ", " + COL_IS_DURABLE + ", " + + COL_IS_NONCONCURRENT + ", " + COL_IS_UPDATE_DATA + ", " + + COL_REQUESTS_RECOVERY + ", " + + COL_JOB_DATAMAP + ") " + " VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?, ?, ?, ?, ?, ?, EMPTY_BLOB())"; + + public static final String UPDATE_ORACLE_JOB_DETAIL = "UPDATE " + + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " SET " + + COL_DESCRIPTION + " = ?, " + COL_JOB_CLASS + " = ?, " + + COL_IS_DURABLE + " = ?, " + COL_IS_NONCONCURRENT + " = ?, " + + COL_IS_UPDATE_DATA + " = ?, " + COL_REQUESTS_RECOVERY + " = ?, " + + COL_JOB_DATAMAP + " = EMPTY_BLOB() " + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + COL_JOB_GROUP + " = ?"; + + public static final String UPDATE_ORACLE_JOB_DETAIL_BLOB = "UPDATE " + + TABLE_PREFIX_SUBST + TABLE_JOB_DETAILS + " SET " + + COL_JOB_DATAMAP + " = ? " + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + + " = ? AND " + COL_JOB_GROUP + " = ?"; + + public static final String SELECT_ORACLE_JOB_DETAIL_BLOB = "SELECT " + + COL_JOB_DATAMAP + " FROM " + TABLE_PREFIX_SUBST + + TABLE_JOB_DETAILS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_JOB_NAME + " = ? AND " + + COL_JOB_GROUP + " = ? FOR UPDATE"; + + public static final String UPDATE_ORACLE_TRIGGER = "UPDATE " + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + COL_JOB_NAME + + " = ?, " + COL_JOB_GROUP + " = ?, " + + COL_DESCRIPTION + " = ?, " + COL_NEXT_FIRE_TIME + " = ?, " + + COL_PREV_FIRE_TIME + " = ?, " + COL_TRIGGER_STATE + " = ?, " + + COL_TRIGGER_TYPE + " = ?, " + COL_START_TIME + " = ?, " + + COL_END_TIME + " = ?, " + COL_CALENDAR_NAME + " = ?, " + + COL_MISFIRE_INSTRUCTION + " = ?, " + + COL_PRIORITY + " = ? WHERE " + + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + + public static final String SELECT_ORACLE_TRIGGER_JOB_DETAIL_BLOB = "SELECT " + + COL_JOB_DATAMAP + " FROM " + TABLE_PREFIX_SUBST + + TABLE_TRIGGERS + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + " = ? AND " + + COL_TRIGGER_GROUP + " = ? FOR UPDATE"; + + public static final String UPDATE_ORACLE_TRIGGER_JOB_DETAIL_BLOB = "UPDATE " + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + + COL_JOB_DATAMAP + " = ? " + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + public static final String UPDATE_ORACLE_TRIGGER_JOB_DETAIL_EMPTY_BLOB = "UPDATE " + + TABLE_PREFIX_SUBST + TABLE_TRIGGERS + " SET " + + COL_JOB_DATAMAP + " = EMPTY_BLOB() " + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_TRIGGER_NAME + + " = ? AND " + COL_TRIGGER_GROUP + " = ?"; + + + public static final String INSERT_ORACLE_CALENDAR = "INSERT INTO " + + TABLE_PREFIX_SUBST + TABLE_CALENDARS + " (" + COL_SCHEDULER_NAME + ", " + + COL_CALENDAR_NAME + ", " + COL_CALENDAR + ") " + + " VALUES(" + SCHED_NAME_SUBST + ", ?, EMPTY_BLOB())"; + + public static final String SELECT_ORACLE_CALENDAR_BLOB = "SELECT " + + COL_CALENDAR + " FROM " + TABLE_PREFIX_SUBST + TABLE_CALENDARS + + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_CALENDAR_NAME + " = ? FOR UPDATE"; + + public static final String UPDATE_ORACLE_CALENDAR_BLOB = "UPDATE " + + TABLE_PREFIX_SUBST + TABLE_CALENDARS + " SET " + COL_CALENDAR + + " = ? " + " WHERE " + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST + + " AND " + COL_CALENDAR_NAME + " = ?"; + + //--------------------------------------------------------------------------- + // protected methods that can be overridden by subclasses + //--------------------------------------------------------------------------- + + @Override + protected Object getObjectFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { + + Object obj = null; + InputStream binaryInput = rs.getBinaryStream(colName); + if (binaryInput != null) { + ObjectInputStream in = new ObjectInputStream(binaryInput); + try { + obj = in.readObject(); + } finally { + in.close(); + } + } + + return obj; + } + + @Override + public int insertJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException { + + ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); + byte[] data = baos.toByteArray(); + PreparedStatement ps = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(INSERT_ORACLE_JOB_DETAIL)); + ps.setString(1, job.getKey().getName()); + ps.setString(2, job.getKey().getGroup()); + ps.setString(3, job.getDescription()); + ps.setString(4, job.getJobClass().getName()); + setBoolean(ps, 5, job.isDurable()); + setBoolean(ps, 6, job.isConcurrentExectionDisallowed()); + setBoolean(ps, 7, job.isPersistJobDataAfterExecution()); + setBoolean(ps, 8, job.requestsRecovery()); + + ps.executeUpdate(); + ps.close(); + + ps = conn.prepareStatement(rtp(SELECT_ORACLE_JOB_DETAIL_BLOB)); + ps.setString(1, job.getKey().getName()); + ps.setString(2, job.getKey().getGroup()); + + rs = ps.executeQuery(); + + int res = 0; + + Blob dbBlob = null; + if (rs.next()) { + dbBlob = writeDataToBlob(rs, 1, data); + } else { + return res; + } + + rs.close(); + ps.close(); + + ps = conn.prepareStatement(rtp(UPDATE_ORACLE_JOB_DETAIL_BLOB)); + ps.setBlob(1, dbBlob); + ps.setString(2, job.getKey().getName()); + ps.setString(3, job.getKey().getGroup()); + + res = ps.executeUpdate(); + + return res; + } finally { + closeResultSet(rs); + closeStatement(ps); + } + + } + + @Override + protected Object getJobDataFromBlob(ResultSet rs, String colName) + throws ClassNotFoundException, IOException, SQLException { + + if (canUseProperties()) { + InputStream binaryInput = rs.getBinaryStream(colName); + return binaryInput; + } + + return getObjectFromBlob(rs, colName); + } + + @Override + public int updateJobDetail(Connection conn, JobDetail job) + throws IOException, SQLException { + + ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); + byte[] data = baos.toByteArray(); + + PreparedStatement ps = null; + PreparedStatement ps2 = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(UPDATE_ORACLE_JOB_DETAIL)); + ps.setString(1, job.getDescription()); + ps.setString(2, job.getJobClass().getName()); + setBoolean(ps, 3, job.isDurable()); + setBoolean(ps, 4, job.isConcurrentExectionDisallowed()); + setBoolean(ps, 5, job.isPersistJobDataAfterExecution()); + setBoolean(ps, 6, job.requestsRecovery()); + ps.setString(7, job.getKey().getName()); + ps.setString(8, job.getKey().getGroup()); + + ps.executeUpdate(); + ps.close(); + + ps = conn.prepareStatement(rtp(SELECT_ORACLE_JOB_DETAIL_BLOB)); + ps.setString(1, job.getKey().getName()); + ps.setString(2, job.getKey().getGroup()); + + rs = ps.executeQuery(); + + int res = 0; + + if (rs.next()) { + Blob dbBlob = writeDataToBlob(rs, 1, data); + ps2 = conn.prepareStatement(rtp(UPDATE_ORACLE_JOB_DETAIL_BLOB)); + + ps2.setBlob(1, dbBlob); + ps2.setString(2, job.getKey().getName()); + ps2.setString(3, job.getKey().getGroup()); + + res = ps2.executeUpdate(); + } + + return res; + + } finally { + closeResultSet(rs); + closeStatement(ps); + closeStatement(ps2); + } + } + + @Override + public int insertTrigger(Connection conn, OperableTrigger trigger, String state, + JobDetail jobDetail) throws SQLException, IOException { + + byte[] data = null; + if (trigger.getJobDataMap().size() > 0) { + data = serializeJobData(trigger.getJobDataMap()).toByteArray(); + } + + PreparedStatement ps = null; + ResultSet rs = null; + + int insertResult = 0; + + try { + ps = conn.prepareStatement(rtp(INSERT_TRIGGER)); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + ps.setString(3, trigger.getJobKey().getName()); + ps.setString(4, trigger.getJobKey().getGroup()); + ps.setString(5, trigger.getDescription()); + ps.setBigDecimal(6, new BigDecimal(String.valueOf(trigger + .getNextFireTime().getTime()))); + long prevFireTime = -1; + if (trigger.getPreviousFireTime() != null) { + prevFireTime = trigger.getPreviousFireTime().getTime(); + } + ps.setBigDecimal(7, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(8, state); + + TriggerPersistenceDelegate tDel = findTriggerPersistenceDelegate(trigger); + + String type = TTYPE_BLOB; + if(tDel != null) + type = tDel.getHandledTriggerTypeDiscriminator(); + ps.setString(9, type); + + ps.setBigDecimal(10, new BigDecimal(String.valueOf(trigger + .getStartTime().getTime()))); + long endTime = 0; + if (trigger.getEndTime() != null) { + endTime = trigger.getEndTime().getTime(); + } + ps.setBigDecimal(11, new BigDecimal(String.valueOf(endTime))); + ps.setString(12, trigger.getCalendarName()); + ps.setInt(13, trigger.getMisfireInstruction()); + ps.setBinaryStream(14, null, 0); + ps.setInt(15, trigger.getPriority()); + + insertResult = ps.executeUpdate(); + + if(data != null) { + ps.close(); + + ps = conn + .prepareStatement(rtp(UPDATE_ORACLE_TRIGGER_JOB_DETAIL_EMPTY_BLOB)); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + ps.executeUpdate(); + ps.close(); + + ps = conn.prepareStatement(rtp(SELECT_ORACLE_TRIGGER_JOB_DETAIL_BLOB)); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + + rs = ps.executeQuery(); + + Blob dbBlob = null; + if (rs.next()) { + dbBlob = writeDataToBlob(rs, 1, data); + } else { + return 0; + } + + rs.close(); + ps.close(); + + ps = conn.prepareStatement(rtp(UPDATE_ORACLE_TRIGGER_JOB_DETAIL_BLOB)); + ps.setBlob(1, dbBlob); + ps.setString(2, trigger.getKey().getName()); + ps.setString(3, trigger.getKey().getGroup()); + + ps.executeUpdate(); + } + + if(tDel == null) + insertBlobTrigger(conn, trigger); + else + tDel.insertExtendedTriggerProperties(conn, trigger, state, jobDetail); + + } finally { + closeResultSet(rs); + closeStatement(ps); + } + + return insertResult; + } + + @Override + public int updateTrigger(Connection conn, OperableTrigger trigger, String state, + JobDetail jobDetail) throws SQLException, IOException { + + // save some clock cycles by unnecessarily writing job data blob ... + boolean updateJobData = trigger.getJobDataMap().isDirty(); + byte[] data = null; + if (updateJobData && trigger.getJobDataMap().size() > 0) { + data = serializeJobData(trigger.getJobDataMap()).toByteArray(); + } + + PreparedStatement ps = null; + PreparedStatement ps2 = null; + ResultSet rs = null; + + int insertResult = 0; + + + try { + ps = conn.prepareStatement(rtp(UPDATE_ORACLE_TRIGGER)); + + ps.setString(1, trigger.getJobKey().getName()); + ps.setString(2, trigger.getJobKey().getGroup()); + ps.setString(3, trigger.getDescription()); + long nextFireTime = -1; + if (trigger.getNextFireTime() != null) { + nextFireTime = trigger.getNextFireTime().getTime(); + } + ps.setBigDecimal(4, new BigDecimal(String.valueOf(nextFireTime))); + long prevFireTime = -1; + if (trigger.getPreviousFireTime() != null) { + prevFireTime = trigger.getPreviousFireTime().getTime(); + } + ps.setBigDecimal(5, new BigDecimal(String.valueOf(prevFireTime))); + ps.setString(6, state); + + TriggerPersistenceDelegate tDel = findTriggerPersistenceDelegate(trigger); + + String type = TTYPE_BLOB; + if(tDel != null) + type = tDel.getHandledTriggerTypeDiscriminator(); + + ps.setString(7, type); + + ps.setBigDecimal(8, new BigDecimal(String.valueOf(trigger + .getStartTime().getTime()))); + long endTime = 0; + if (trigger.getEndTime() != null) { + endTime = trigger.getEndTime().getTime(); + } + ps.setBigDecimal(9, new BigDecimal(String.valueOf(endTime))); + ps.setString(10, trigger.getCalendarName()); + ps.setInt(11, trigger.getMisfireInstruction()); + ps.setInt(12, trigger.getPriority()); + ps.setString(13, trigger.getKey().getName()); + ps.setString(14, trigger.getKey().getGroup()); + + insertResult = ps.executeUpdate(); + + if(updateJobData) { + ps.close(); + + ps = conn + .prepareStatement(rtp(UPDATE_ORACLE_TRIGGER_JOB_DETAIL_EMPTY_BLOB)); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + ps.executeUpdate(); + ps.close(); + + ps = conn.prepareStatement(rtp(SELECT_ORACLE_TRIGGER_JOB_DETAIL_BLOB)); + ps.setString(1, trigger.getKey().getName()); + ps.setString(2, trigger.getKey().getGroup()); + + rs = ps.executeQuery(); + + if (rs.next()) { + Blob dbBlob = writeDataToBlob(rs, 1, data); + ps2 = conn.prepareStatement(rtp(UPDATE_ORACLE_TRIGGER_JOB_DETAIL_BLOB)); + + ps2.setBlob(1, dbBlob); + ps2.setString(2, trigger.getKey().getName()); + ps2.setString(3, trigger.getKey().getGroup()); + + ps2.executeUpdate(); + } + } + + if(tDel == null) + updateBlobTrigger(conn, trigger); + else + tDel.updateExtendedTriggerProperties(conn, trigger, state, jobDetail); + + } finally { + closeResultSet(rs); + closeStatement(ps); + closeStatement(ps2); + } + + return insertResult; + } + + @Override + public int insertCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException { + ByteArrayOutputStream baos = serializeObject(calendar); + + PreparedStatement ps = null; + PreparedStatement ps2 = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(INSERT_ORACLE_CALENDAR)); + ps.setString(1, calendarName); + + ps.executeUpdate(); + ps.close(); + + ps = conn.prepareStatement(rtp(SELECT_ORACLE_CALENDAR_BLOB)); + ps.setString(1, calendarName); + + rs = ps.executeQuery(); + + if (rs.next()) { + Blob dbBlob = writeDataToBlob(rs, 1, baos.toByteArray()); + ps2 = conn.prepareStatement(rtp(UPDATE_ORACLE_CALENDAR_BLOB)); + + ps2.setBlob(1, dbBlob); + ps2.setString(2, calendarName); + + return ps2.executeUpdate(); + } + + return 0; + + } finally { + closeResultSet(rs); + closeStatement(ps); + closeStatement(ps2); + } + } + + @Override + public int updateCalendar(Connection conn, String calendarName, + Calendar calendar) throws IOException, SQLException { + ByteArrayOutputStream baos = serializeObject(calendar); + + PreparedStatement ps = null; + PreparedStatement ps2 = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_ORACLE_CALENDAR_BLOB)); + ps.setString(1, calendarName); + + rs = ps.executeQuery(); + + if (rs.next()) { + Blob dbBlob = writeDataToBlob(rs, 1, baos.toByteArray()); + ps2 = conn.prepareStatement(rtp(UPDATE_ORACLE_CALENDAR_BLOB)); + + ps2.setBlob(1, dbBlob); + ps2.setString(2, calendarName); + + return ps2.executeUpdate(); + } + + return 0; + + } finally { + closeResultSet(rs); + closeStatement(ps); + closeStatement(ps2); + } + } + + @Override + public int updateJobData(Connection conn, JobDetail job) + throws IOException, SQLException { + + ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); + byte[] data = baos.toByteArray(); + + PreparedStatement ps = null; + PreparedStatement ps2 = null; + ResultSet rs = null; + + try { + ps = conn.prepareStatement(rtp(SELECT_ORACLE_JOB_DETAIL_BLOB)); + ps.setString(1, job.getKey().getName()); + ps.setString(2, job.getKey().getGroup()); + + rs = ps.executeQuery(); + + int res = 0; + + if (rs.next()) { + Blob dbBlob = writeDataToBlob(rs, 1, data); + ps2 = conn.prepareStatement(rtp(UPDATE_ORACLE_JOB_DETAIL_BLOB)); + + ps2.setBlob(1, dbBlob); + ps2.setString(2, job.getKey().getName()); + ps2.setString(3, job.getKey().getGroup()); + + res = ps2.executeUpdate(); + } + + return res; + } finally { + closeResultSet(rs); + closeStatement(ps); + closeStatement(ps2); + } + } + + @SuppressWarnings("deprecation") + protected Blob writeDataToBlob(ResultSet rs, int column, byte[] data) throws SQLException { + + Blob blob = rs.getBlob(column); // get blob + + if (blob == null) { + throw new SQLException("Driver's Blob representation is null!"); + } + + if (blob instanceof oracle.sql.BLOB) { // is it an oracle blob? + ((oracle.sql.BLOB) blob).putBytes(1, data); + ((oracle.sql.BLOB) blob).trim(data.length); + return blob; + } else { + throw new SQLException( + "Driver's Blob representation is of an unsupported type: " + + blob.getClass().getName()); + } + } +} + +// EOF Index: 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/oracle/weblogic/WebLogicOracleDelegate.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/oracle/weblogic/WebLogicOracleDelegate.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/jdbcjobstore/oracle/weblogic/WebLogicOracleDelegate.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,75 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. Inc. + * + * 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 org.quartz.impl.jdbcjobstore.oracle.weblogic; + +import org.slf4j.Logger; +import org.quartz.impl.jdbcjobstore.oracle.OracleDelegate; +import org.quartz.spi.ClassLoadHelper; + +import java.lang.reflect.Method; +import java.sql.Blob; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Handle Blobs correctly when Oracle is being used inside of Weblogic 8.1, + * as discussed at: http://edocs.bea.com/wls/docs81/jdbc/thirdparty.html#1043705 + * + * @see org.quartz.impl.jdbcjobstore.WebLogicDelegate + * @author James House + * @author Igor Fedulov igor@fedulov.com + */ +public class WebLogicOracleDelegate extends OracleDelegate { + + /** + * Check for the Weblogic Blob wrapper, and handle accordingly... + */ + @Override + protected Blob writeDataToBlob(ResultSet rs, int column, byte[] data) throws SQLException { + Blob blob = rs.getBlob(column); + + if (blob == null) { + throw new SQLException("Driver's Blob representation is null!"); + } + + // handle thin driver's blob + if (blob instanceof weblogic.jdbc.vendor.oracle.OracleThinBlob) { + ((weblogic.jdbc.vendor.oracle.OracleThinBlob) blob).putBytes(1, data); + return blob; + } else if(blob.getClass().getPackage().getName().startsWith("weblogic.")) { + // (more slowly) handle blob for wrappers of other variations of drivers... + try { + // try to find putBytes method... + Method m = blob.getClass().getMethod("putBytes", new Class[] {long.class, byte[].class}); + m.invoke(blob, new Object[] {new Long(1), data}); + } catch (Exception e) { + try { + // Added this logic to the original code from OpenSymphony + // putBytes method does not exist. Try setBytes + Method m = blob.getClass().getMethod("setBytes", new Class[] { long.class, byte[].class }); + m.invoke(blob, new Object[] { new Long(1), data }); + } catch (Exception e2) { + throw new SQLException("Unable to find putBytes(long,byte[]) or setBytes(long,byte[]) methods on blob: " + e2); + } + } + return blob; + } else { + return super.writeDataToBlob(rs, column, data); + } + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/matchers/AndMatcher.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/matchers/AndMatcher.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/matchers/AndMatcher.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,95 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.matchers; + +import org.quartz.Matcher; +import org.quartz.utils.Key; + +/** + * Matches using an AND operator on two Matcher operands. + * + * @author jhouse + */ +public class AndMatcher> implements Matcher { + + private static final long serialVersionUID = 4697276220890670941L; + + protected Matcher leftOperand; + protected Matcher rightOperand; + + protected AndMatcher(Matcher leftOperand, Matcher rightOperand) { + if(leftOperand == null || rightOperand == null) + throw new IllegalArgumentException("Two non-null operands required!"); + + this.leftOperand = leftOperand; + this.rightOperand = rightOperand; + } + + /** + * Create an AndMatcher that depends upon the result of both of the given matchers. + */ + public static > AndMatcher and(Matcher leftOperand, Matcher rightOperand) { + return new AndMatcher(leftOperand, rightOperand); + } + + public boolean isMatch(T key) { + + return leftOperand.isMatch(key) && rightOperand.isMatch(key); + } + + public Matcher getLeftOperand() { + return leftOperand; + } + + public Matcher getRightOperand() { + return rightOperand; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((leftOperand == null) ? 0 : leftOperand.hashCode()); + result = prime * result + + ((rightOperand == null) ? 0 : rightOperand.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AndMatcher other = (AndMatcher) obj; + if (leftOperand == null) { + if (other.leftOperand != null) + return false; + } else if (!leftOperand.equals(other.leftOperand)) + return false; + if (rightOperand == null) { + if (other.rightOperand != null) + return false; + } else if (!rightOperand.equals(other.rightOperand)) + return false; + return true; + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/matchers/EverythingMatcher.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/matchers/EverythingMatcher.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/matchers/EverythingMatcher.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,68 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.matchers; + +import org.quartz.JobKey; +import org.quartz.Matcher; +import org.quartz.TriggerKey; +import org.quartz.utils.Key; + +/** + * Matches on the complete key being equal (both name and group). + * + * @author jhouse + */ +public class EverythingMatcher> implements Matcher { + + private static final long serialVersionUID = 202300056681974058L; + + protected EverythingMatcher() { + } + + /** + * Create an EverythingMatcher that matches all jobs. + */ + public static EverythingMatcher allJobs() { + return new EverythingMatcher(); + } + + /** + * Create an EverythingMatcher that matches all triggers. + */ + public static EverythingMatcher allTriggers() { + return new EverythingMatcher(); + } + + public boolean isMatch(T key) { + return true; + } + + @Override + public boolean equals(Object obj) { + if(obj == null) + return false; + + return obj.getClass().equals(getClass()); + } + + @Override + public int hashCode() { + return getClass().getName().hashCode(); + } + + +} Index: 3rdParty_sources/quartz/org/quartz/impl/matchers/GroupMatcher.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/matchers/GroupMatcher.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/matchers/GroupMatcher.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,146 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.matchers; + +import org.quartz.JobKey; +import org.quartz.TriggerKey; +import org.quartz.utils.Key; + +/** + * Matches on group (ignores name) property of Keys. + * + * @author jhouse + */ +public class GroupMatcher> extends StringMatcher { + + private static final long serialVersionUID = -3275767650469343849L; + + protected GroupMatcher(String compareTo, StringOperatorName compareWith) { + super(compareTo, compareWith); + } + + /** + * Create a GroupMatcher that matches groups equaling the given string. + */ + public static > GroupMatcher groupEquals(String compareTo) { + return new GroupMatcher(compareTo, StringOperatorName.EQUALS); + } + + /** + * Create a GroupMatcher that matches job groups equaling the given string. + */ + public static GroupMatcher jobGroupEquals(String compareTo) { + return GroupMatcher.groupEquals(compareTo); + } + + /** + * Create a GroupMatcher that matches trigger groups equaling the given string. + */ + public static GroupMatcher triggerGroupEquals(String compareTo) { + return GroupMatcher.groupEquals(compareTo); + } + + /** + * Create a GroupMatcher that matches groups starting with the given string. + */ + public static > GroupMatcher groupStartsWith(String compareTo) { + return new GroupMatcher(compareTo, StringOperatorName.STARTS_WITH); + } + + /** + * Create a GroupMatcher that matches job groups starting with the given string. + */ + public static GroupMatcher jobGroupStartsWith(String compareTo) { + return GroupMatcher.groupStartsWith(compareTo); + } + + /** + * Create a GroupMatcher that matches trigger groups starting with the given string. + */ + public static GroupMatcher triggerGroupStartsWith(String compareTo) { + return GroupMatcher.groupStartsWith(compareTo); + } + + /** + * Create a GroupMatcher that matches groups ending with the given string. + */ + public static > GroupMatcher groupEndsWith(String compareTo) { + return new GroupMatcher(compareTo, StringOperatorName.ENDS_WITH); + } + + /** + * Create a GroupMatcher that matches job groups ending with the given string. + */ + public static GroupMatcher jobGroupEndsWith(String compareTo) { + return GroupMatcher.groupEndsWith(compareTo); + } + + /** + * Create a GroupMatcher that matches trigger groups ending with the given string. + */ + public static GroupMatcher triggerGroupEndsWith(String compareTo) { + return GroupMatcher.groupEndsWith(compareTo); + } + + /** + * Create a GroupMatcher that matches groups containing the given string. + */ + public static > GroupMatcher groupContains(String compareTo) { + return new GroupMatcher(compareTo, StringOperatorName.CONTAINS); + } + + /** + * Create a GroupMatcher that matches job groups containing the given string. + */ + public static GroupMatcher jobGroupContains(String compareTo) { + return GroupMatcher.groupContains(compareTo); + } + + /** + * Create a GroupMatcher that matches trigger groups containing the given string. + */ + public static GroupMatcher triggerGroupContains(String compareTo) { + return GroupMatcher.groupContains(compareTo); + } + + /** + * Create a GroupMatcher that matches groups starting with the given string. + */ + public static > GroupMatcher anyGroup() { + return new GroupMatcher("", StringOperatorName.ANYTHING); + } + + /** + * Create a GroupMatcher that matches job groups starting with the given string. + */ + public static GroupMatcher anyJobGroup() { + return GroupMatcher.anyGroup(); + } + + /** + * Create a GroupMatcher that matches trigger groups starting with the given string. + */ + public static GroupMatcher anyTriggerGroup() { + return GroupMatcher.anyGroup(); + } + + @Override + protected String getValue(T key) { + return key.getGroup(); + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/matchers/KeyMatcher.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/matchers/KeyMatcher.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/matchers/KeyMatcher.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,79 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.matchers; + +import org.quartz.Matcher; +import org.quartz.utils.Key; + +/** + * Matches on the complete key being equal (both name and group). + * + * @author jhouse + */ +public class KeyMatcher> implements Matcher { + + private static final long serialVersionUID = 1230009869074992437L; + + protected T compareTo; + + protected KeyMatcher(T compareTo) { + this.compareTo = compareTo; + } + + /** + * Create a KeyMatcher that matches Keys that equal the given key. + */ + public static > KeyMatcher keyEquals(U compareTo) { + return new KeyMatcher(compareTo); + } + + public boolean isMatch(T key) { + + return compareTo.equals(key); + } + + public T getCompareToValue() { + return compareTo; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((compareTo == null) ? 0 : compareTo.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + KeyMatcher other = (KeyMatcher) obj; + if (compareTo == null) { + if (other.compareTo != null) + return false; + } else if (!compareTo.equals(other.compareTo)) + return false; + return true; + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/matchers/NameMatcher.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/matchers/NameMatcher.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/matchers/NameMatcher.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,125 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.matchers; + +import org.quartz.JobKey; +import org.quartz.TriggerKey; +import org.quartz.utils.Key; + +/** + * Matches on name (ignores group) property of Keys. + * + * @author jhouse + */ +public class NameMatcher> extends StringMatcher { + + private static final long serialVersionUID = -33104959459613480L; + + protected NameMatcher(String compareTo, StringOperatorName compareWith) { + super(compareTo, compareWith); + } + + /** + * Create a NameMatcher that matches names equaling the given string. + */ + public static > NameMatcher nameEquals(String compareTo) { + return new NameMatcher(compareTo, StringOperatorName.EQUALS); + } + + /** + * Create a NameMatcher that matches job names equaling the given string. + */ + public static NameMatcher jobNameEquals(String compareTo) { + return NameMatcher.nameEquals(compareTo); + } + + /** + * Create a NameMatcher that matches trigger names equaling the given string. + */ + public static NameMatcher triggerNameEquals(String compareTo) { + return NameMatcher.nameEquals(compareTo); + } + + /** + * Create a NameMatcher that matches names starting with the given string. + */ + public static > NameMatcher nameStartsWith(String compareTo) { + return new NameMatcher(compareTo, StringOperatorName.STARTS_WITH); + } + + /** + * Create a NameMatcher that matches job names starting with the given string. + */ + public static NameMatcher jobNameStartsWith(String compareTo) { + return NameMatcher.nameStartsWith(compareTo); + } + + /** + * Create a NameMatcher that matches trigger names starting with the given string. + */ + public static NameMatcher triggerNameStartsWith(String compareTo) { + return NameMatcher.nameStartsWith(compareTo); + } + + /** + * Create a NameMatcher that matches names ending with the given string. + */ + public static > NameMatcher nameEndsWith(String compareTo) { + return new NameMatcher(compareTo, StringOperatorName.ENDS_WITH); + } + + /** + * Create a NameMatcher that matches job names ending with the given string. + */ + public static NameMatcher jobNameEndsWith(String compareTo) { + return NameMatcher.nameEndsWith(compareTo); + } + + /** + * Create a NameMatcher that matches trigger names ending with the given string. + */ + public static NameMatcher triggerNameEndsWith(String compareTo) { + return NameMatcher.nameEndsWith(compareTo); + } + + /** + * Create a NameMatcher that matches names containing the given string. + */ + public static > NameMatcher nameContains(String compareTo) { + return new NameMatcher(compareTo, StringOperatorName.CONTAINS); + } + + /** + * Create a NameMatcher that matches job names containing the given string. + */ + public static NameMatcher jobNameContains(String compareTo) { + return NameMatcher.nameContains(compareTo); + } + + /** + * Create a NameMatcher that matches trigger names containing the given string. + */ + public static NameMatcher triggerNameContains(String compareTo) { + return NameMatcher.nameContains(compareTo); + } + + @Override + protected String getValue(T key) { + return key.getName(); + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/matchers/NotMatcher.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/matchers/NotMatcher.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/matchers/NotMatcher.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,80 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.matchers; + +import org.quartz.Matcher; +import org.quartz.utils.Key; + +/** + * Matches using an NOT operator on another Matcher. + * + * @author jhouse + */ +public class NotMatcher> implements Matcher { + + private static final long serialVersionUID = -2856769076151741391L; + + protected Matcher operand; + + protected NotMatcher(Matcher operand) { + if(operand == null) + throw new IllegalArgumentException("Non-null operand required!"); + + this.operand = operand; + } + + /** + * Create a NotMatcher that reverses the result of the given matcher. + */ + public static > NotMatcher not(Matcher operand) { + return new NotMatcher(operand); + } + + public boolean isMatch(T key) { + + return !operand.isMatch(key); + } + + public Matcher getOperand() { + return operand; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((operand == null) ? 0 : operand.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NotMatcher other = (NotMatcher) obj; + if (operand == null) { + if (other.operand != null) + return false; + } else if (!operand.equals(other.operand)) + return false; + return true; + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/matchers/OrMatcher.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/matchers/OrMatcher.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/matchers/OrMatcher.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,95 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.matchers; + +import org.quartz.Matcher; +import org.quartz.utils.Key; + +/** + * Matches using an OR operator on two Matcher operands. + * + * @author jhouse + */ +public class OrMatcher> implements Matcher { + + private static final long serialVersionUID = -2867392824539403712L; + + protected Matcher leftOperand; + protected Matcher rightOperand; + + protected OrMatcher(Matcher leftOperand, Matcher rightOperand) { + if(leftOperand == null || rightOperand == null) + throw new IllegalArgumentException("Two non-null operands required!"); + + this.leftOperand = leftOperand; + this.rightOperand = rightOperand; + } + + /** + * Create an OrMatcher that depends upon the result of at least one of the given matchers. + */ + public static > OrMatcher or(Matcher leftOperand, Matcher rightOperand) { + return new OrMatcher(leftOperand, rightOperand); + } + + public boolean isMatch(T key) { + + return leftOperand.isMatch(key) || rightOperand.isMatch(key); + } + + public Matcher getLeftOperand() { + return leftOperand; + } + + public Matcher getRightOperand() { + return rightOperand; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((leftOperand == null) ? 0 : leftOperand.hashCode()); + result = prime * result + + ((rightOperand == null) ? 0 : rightOperand.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + OrMatcher other = (OrMatcher) obj; + if (leftOperand == null) { + if (other.leftOperand != null) + return false; + } else if (!leftOperand.equals(other.leftOperand)) + return false; + if (rightOperand == null) { + if (other.rightOperand != null) + return false; + } else if (!rightOperand.equals(other.rightOperand)) + return false; + return true; + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/matchers/StringMatcher.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/matchers/StringMatcher.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/matchers/StringMatcher.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,132 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.matchers; + +import org.quartz.Matcher; +import org.quartz.utils.Key; + +/** + * An abstract base class for some types of matchers. + * + * @author jhouse + */ +public abstract class StringMatcher> implements Matcher { + + private static final long serialVersionUID = -2757924162611145836L; + + public enum StringOperatorName { + + EQUALS { + @Override + public boolean evaluate(final String value, final String compareTo) { + return value.equals(compareTo); + } + }, + + STARTS_WITH { + @Override + public boolean evaluate(final String value, final String compareTo) { + return value.startsWith(compareTo); + } + }, + + ENDS_WITH { + @Override + public boolean evaluate(final String value, final String compareTo) { + return value.endsWith(compareTo); + } + }, + + CONTAINS { + @Override + public boolean evaluate(final String value, final String compareTo) { + return value.contains(compareTo); + } + }, + + ANYTHING { + @Override + public boolean evaluate(final String value, final String compareTo) { + return true; + } + }; + + public abstract boolean evaluate(String value, String compareTo); + } + + protected String compareTo; + protected StringOperatorName compareWith; + + protected StringMatcher(String compareTo, StringOperatorName compareWith) { + if(compareTo == null) + throw new IllegalArgumentException("CompareTo value cannot be null!"); + if(compareWith == null) + throw new IllegalArgumentException("CompareWith operator cannot be null!"); + + this.compareTo = compareTo; + this.compareWith = compareWith; + } + + protected abstract String getValue(T key); + + public boolean isMatch(T key) { + + return compareWith.evaluate(getValue(key), compareTo); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((compareTo == null) ? 0 : compareTo.hashCode()); + result = prime * result + + ((compareWith == null) ? 0 : compareWith.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StringMatcher other = (StringMatcher) obj; + if (compareTo == null) { + if (other.compareTo != null) + return false; + } else if (!compareTo.equals(other.compareTo)) + return false; + if (compareWith == null) { + if (other.compareWith != null) + return false; + } else if (!compareWith.equals(other.compareWith)) + return false; + return true; + } + + public String getCompareToValue() { + return compareTo; + } + + public StringOperatorName getCompareWithOperator() { + return compareWith; + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/package.html =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/impl/package.html (.../package.html) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/impl/package.html (.../package.html) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -11,8 +11,8 @@


-See the Quartz project - at Open Symphony for more information. +See the Quartz project + for more information. Index: 3rdParty_sources/quartz/org/quartz/impl/triggers/AbstractTrigger.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/triggers/AbstractTrigger.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/triggers/AbstractTrigger.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,889 @@ + +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.triggers; + +import java.util.Date; + +import org.quartz.Calendar; +import org.quartz.CronTrigger; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobKey; +import org.quartz.ScheduleBuilder; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import org.quartz.TriggerUtils; +import org.quartz.spi.OperableTrigger; + + +/** + *

+ * The base abstract class to be extended by all Triggers. + *

+ * + *

+ * Triggers s have a name and group associated with them, which + * should uniquely identify them within a single {@link Scheduler}. + *

+ * + *

+ * Triggers 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 Jobs by placing contents + * into the JobDataMap on the Trigger. + *

+ * + * @author James House + * @author Sharada Jambula + */ +public abstract class AbstractTrigger implements OperableTrigger { + + private static final long serialVersionUID = -3904243490805975570L; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private String name; + + private String group = Scheduler.DEFAULT_GROUP; + + private String jobName; + + private String jobGroup = Scheduler.DEFAULT_GROUP; + + private String description; + + private JobDataMap jobDataMap; + + @SuppressWarnings("unused") + private boolean volatility = false; // still here for serialization backward compatibility + + private String calendarName = null; + + private String fireInstanceId = null; + + private int misfireInstruction = MISFIRE_INSTRUCTION_SMART_POLICY; + + private int priority = DEFAULT_PRIORITY; + + private transient TriggerKey key = null; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + + + /** + *

+ * Create a Trigger with no specified name, group, or {@link 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}. + *

+ */ + public AbstractTrigger() { + // do nothing... + } + + /** + *

+ * Create a Trigger with the given name, and default 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}. + *

+ * + * @exception IllegalArgumentException + * if name is null or empty, or the group is an empty string. + */ + public AbstractTrigger(String name) { + setName(name); + setGroup(null); + } + + /** + *

+ * 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}. + *

+ * + * @param group if null, Scheduler.DEFAULT_GROUP will be used. + * + * @exception IllegalArgumentException + * if name is null or empty, or the group is an empty string. + */ + public AbstractTrigger(String name, String group) { + setName(name); + setGroup(group); + } + + /** + *

+ * Create a Trigger with the given name, and group. + *

+ * + * @param group if null, Scheduler.DEFAULT_GROUP will be used. + * + * @exception IllegalArgumentException + * if name is null or empty, or the group is an empty string. + */ + public AbstractTrigger(String name, String group, String jobName, String jobGroup) { + setName(name); + setGroup(group); + setJobName(jobName); + setJobGroup(jobGroup); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Get the name of this Trigger. + *

+ */ + public String getName() { + return name; + } + + /** + *

+ * Set the name of this Trigger. + *

+ * + * @exception IllegalArgumentException + * if name is null or empty. + */ + public void setName(String name) { + if (name == null || name.trim().length() == 0) { + throw new IllegalArgumentException( + "Trigger name cannot be null or empty."); + } + + this.name = name; + this.key = null; + } + + /** + *

+ * Get the group of this Trigger. + *

+ */ + public String getGroup() { + return group; + } + + /** + *

+ * Set the name of this Trigger. + *

+ * + * @param group if 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; + this.key = null; + } + + public void setKey(TriggerKey key) { + setName(key.getName()); + setGroup(key.getGroup()); + this.key = key; + } + + /** + *

+ * Get the name of the associated {@link org.quartz.JobDetail}. + *

+ */ + public String getJobName() { + return jobName; + } + + /** + *

+ * Set the name of the associated {@link org.quartz.JobDetail}. + *

+ * + * @exception IllegalArgumentException + * if jobName is null or empty. + */ + public void setJobName(String jobName) { + if (jobName == null || jobName.trim().length() == 0) { + throw new IllegalArgumentException( + "Job name cannot be null or empty."); + } + + this.jobName = jobName; + } + + /** + *

+ * Get the name of the associated {@link org.quartz.JobDetail}'s + * group. + *

+ */ + public String getJobGroup() { + return jobGroup; + } + + /** + *

+ * Set the name of the associated {@link org.quartz.JobDetail}'s + * group. + *

+ * + * @param jobGroup if 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; + } + + public void setJobKey(JobKey key) { + setJobName(key.getName()); + setJobGroup(key.getGroup()); + } + + + /** + *

+ * Returns the 'full name' of the Trigger in the format + * "group.name". + *

+ */ + public String getFullName() { + return group + "." + name; + } + + public TriggerKey getKey() { + if(key == null) { + if(getName() == null) + return null; + key = new TriggerKey(getName(), getGroup()); + } + + return key; + } + + public JobKey getJobKey() { + if(getJobName() == null) + return null; + + return new JobKey(getJobName(), getJobGroup()); + } + + /** + *

+ * Returns the 'full name' of the Job that the Trigger + * points to, in the format "group.name". + *

+ */ + public String getFullJobName() { + return jobGroup + "." + jobName; + } + + /** + *

+ * Return the description given to the Trigger instance by + * its creator (if any). + *

+ * + * @return null if no description was set. + */ + public String getDescription() { + return description; + } + + /** + *

+ * 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. + *

+ */ + public void setDescription(String description) { + this.description = description; + } + + /** + *

+ * Associate the {@link Calendar} with the given name with + * this Trigger. + *

+ * + * @param calendarName + * use 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. + *

+ * + * @return 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. + *

+ */ + public JobDataMap getJobDataMap() { + if (jobDataMap == null) { + jobDataMap = new JobDataMap(); + } + return jobDataMap; + } + + + /** + *

+ * Set the JobDataMap to be associated with the + * Trigger. + *

+ */ + public void setJobDataMap(JobDataMap jobDataMap) { + this.jobDataMap = jobDataMap; + } + + /** + * The priority of a Trigger acts as a tiebreaker such that if + * two Triggers 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. + *

+ * + * @see #DEFAULT_PRIORITY + */ + public int getPriority() { + return priority; + } + + + /** + * The priority of a Trigger acts as a tie breaker such that if + * two Triggers 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. + *

+ * + * @see #DEFAULT_PRIORITY + */ + public void setPriority(int priority) { + this.priority = priority; + } + + /** + *

+ * 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). + *

+ * + * @see #executionComplete(JobExecutionContext, JobExecutionException) + */ + public abstract void triggered(Calendar calendar); + + /** + *

+ * 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. + *

+ * + * @return the first time at which the 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 org.quartz.JobDetail} associated with the Trigger + * in order to get the final instruction code from the trigger. + *

+ * + * @param context + * is the 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 CompletedExecutionInstruction constants. + * + * @see org.quartz.Trigger.CompletedExecutionInstruction + * @see #triggered(Calendar) + */ + public CompletedExecutionInstruction executionComplete(JobExecutionContext context, + JobExecutionException result) + { + if (result != null && result.refireImmediately()) { + return CompletedExecutionInstruction.RE_EXECUTE_JOB; + } + + if (result != null && result.unscheduleFiringTrigger()) { + return CompletedExecutionInstruction.SET_TRIGGER_COMPLETE; + } + + if (result != null && result.unscheduleAllTriggers()) { + return CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_COMPLETE; + } + + if (!mayFireAgain()) { + return CompletedExecutionInstruction.DELETE_TRIGGER; + } + + return CompletedExecutionInstruction.NOOP; + } + + /** + *

+ * 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 org.quartz.spi.JobStore}. + *

+ */ + public abstract boolean mayFireAgain(); + + /** + *

+ * Get the time at which the Trigger should occur. + *

+ */ + public abstract Date getStartTime(); + + /** + *

+ * 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); + + /** + *

+ * Set the time at which the Trigger should quit repeating - + * regardless of any remaining repeats (based on the trigger's particular + * repeat settings). + *

+ * + * @see TriggerUtils#computeEndTimeToAllowParticularNumberOfFirings(org.quartz.spi.OperableTrigger, org.quartz.Calendar, int) + */ + public abstract void setEndTime(Date endTime); + + /** + *

+ * Get the time at which the Trigger should quit repeating - + * regardless of any remaining repeats (based on the trigger's particular + * repeat settings). + *

+ * + * @see #getFinalFireTime() + */ + public abstract Date getEndTime(); + + /** + *

+ * 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. + *

+ * + * @see TriggerUtils#computeFireTimesBetween(org.quartz.spi.OperableTrigger, org.quartz.Calendar, java.util.Date, java.util.Date) + */ + public abstract Date getNextFireTime(); + + /** + *

+ * 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. + *

+ */ + public abstract Date getFireTimeAfter(Date afterTime); + + /** + *

+ * 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. + *

+ * + * @see #MISFIRE_INSTRUCTION_SMART_POLICY + * @see #updateAfterMisfire(Calendar) + * @see SimpleTrigger + * @see CronTrigger + */ + public void setMisfireInstruction(int misfireInstruction) { + if (!validateMisfireInstruction(misfireInstruction)) { + throw new IllegalArgumentException( + "The misfire instruction code is invalid for this type of trigger."); + } + this.misfireInstruction = misfireInstruction; + } + + protected abstract boolean validateMisfireInstruction(int candidateMisfireInstruction); + + /** + *

+ * 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. + *

+ * + * @see #MISFIRE_INSTRUCTION_SMART_POLICY + * @see #updateAfterMisfire(Calendar) + * @see SimpleTrigger + * @see CronTrigger + */ + public int getMisfireInstruction() { + return misfireInstruction; + } + + /** + *

+ * 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. + *

+ */ + public abstract void updateAfterMisfire(Calendar cal); + + /** + *

+ * 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). + *

+ * + * @param cal the modifying calendar + */ + public abstract void updateWithNewCalendar(Calendar cal, long misfireThreshold); + + /** + *

+ * 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"); + } + + if (group == null) { + throw new SchedulerException("Trigger's group cannot be null"); + } + + if (jobName == null) { + throw new SchedulerException( + "Trigger's related Job's name cannot be null"); + } + + if (jobGroup == null) { + throw new SchedulerException( + "Trigger's related Job's group cannot be null"); + } + } + + /** + *

+ * This method should not be used by the Quartz client. + *

+ * + *

+ * Usable by {@link org.quartz.spi.JobStore} + * implementations, in order to facilitate 'recognizing' instances of fired + * Trigger s as their jobs complete execution. + *

+ * + * + */ + public void setFireInstanceId(String id) { + this.fireInstanceId = id; + } + + /** + *

+ * This method should not be used by the Quartz client. + *

+ */ + public String getFireInstanceId() { + return fireInstanceId; + } + + /** + *

+ * Return a simple string representation of this object. + *

+ */ + @Override + public String toString() { + return "Trigger '" + getFullName() + "': triggerClass: '" + + getClass().getName() + " calendar: '" + getCalendarName() + + "' misfireInstruction: " + getMisfireInstruction() + + " nextFireTime: " + getNextFireTime(); + } + + /** + *

+ * Compare the next fire time of this Trigger to that of + * another by comparing their keys, or in other words, sorts them + * according to the natural (i.e. alphabetical) order of their keys. + *

+ */ + public int compareTo(Trigger other) { + + if(other.getKey() == null && getKey() == null) + return 0; + if(other.getKey() == null) + return -1; + if(getKey() == null) + return 1; + + return getKey().compareTo(other.getKey()); + } + + /** + * Trigger equality is based upon the equality of the TriggerKey. + * + * @return true if the key of this Trigger equals that of the given Trigger. + */ + @Override + public boolean equals(Object o) { + if(!(o instanceof Trigger)) + return false; + + Trigger other = (Trigger)o; + + return !(other.getKey() == null || getKey() == null) && getKey().equals(other.getKey()); + + } + + + @Override + public int hashCode() { + if(getKey() == null) + return super.hashCode(); + + return getKey().hashCode(); + } + + @Override + public Object clone() { + AbstractTrigger copy; + try { + copy = (AbstractTrigger) super.clone(); + + // Shallow copy the jobDataMap. Note that this means that if a user + // modifies a value object in this map from the cloned Trigger + // they will also be modifying this Trigger. + if (jobDataMap != null) { + copy.jobDataMap = (JobDataMap)jobDataMap.clone(); + } + + } catch (CloneNotSupportedException ex) { + throw new IncompatibleClassChangeError("Not Cloneable."); + } + return copy; + } + + public TriggerBuilder getTriggerBuilder() { + return TriggerBuilder.newTrigger() + .forJob(getJobKey()) + .modifiedByCalendar(getCalendarName()) + .usingJobData(getJobDataMap()) + .withDescription(getDescription()) + .endAt(getEndTime()) + .withIdentity(getKey()) + .withPriority(getPriority()) + .startAt(getStartTime()) + .withSchedule(getScheduleBuilder()); + } + + public abstract ScheduleBuilder getScheduleBuilder(); +} Index: 3rdParty_sources/quartz/org/quartz/impl/triggers/CalendarIntervalTriggerImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/triggers/CalendarIntervalTriggerImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/triggers/CalendarIntervalTriggerImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,975 @@ + +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.triggers; + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +import org.quartz.CalendarIntervalScheduleBuilder; +import org.quartz.CalendarIntervalTrigger; +import org.quartz.CronTrigger; +import org.quartz.DateBuilder.IntervalUnit; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.ScheduleBuilder; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.quartz.TriggerUtils; + + +/** + *

A concrete {@link Trigger} that is used to fire a {@link org.quartz.JobDetail} + * based upon repeating calendar time intervals.

+ * + *

The trigger will fire every N (see {@link #setRepeatInterval(int)} ) units of calendar time + * (see {@link #setRepeatIntervalUnit(org.quartz.DateBuilder.IntervalUnit)}) as specified in the trigger's definition. + * This trigger can achieve schedules that are not possible with {@link SimpleTrigger} (e.g + * because months are not a fixed number of seconds) or {@link CronTrigger} (e.g. because + * "every 5 months" is not an even divisor of 12).

+ * + *

If you use an interval unit of MONTH then care should be taken when setting + * a startTime value that is on a day near the end of the month. For example, + * if you choose a start time that occurs on January 31st, and have a trigger with unit + * MONTH and interval 1, then the next fire time will be February 28th, + * and the next time after that will be March 28th - and essentially each subsequent firing will + * occur on the 28th of the month, even if a 31st day exists. If you want a trigger that always + * fires on the last day of the month - regardless of the number of days in the month, + * you should use CronTrigger.

+ * + * @see Trigger + * @see CronTrigger + * @see SimpleTrigger + * @see TriggerUtils + * + * @since 1.7 + * + * @author James House + */ +public class CalendarIntervalTriggerImpl extends AbstractTrigger implements CalendarIntervalTrigger, CoreTrigger { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private static final long serialVersionUID = -2635982274232850343L; + + + private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private Date startTime = null; + + private Date endTime = null; + + private Date nextFireTime = null; + + private Date previousFireTime = null; + + private int repeatInterval = 0; + + private IntervalUnit repeatIntervalUnit = IntervalUnit.DAY; + + private TimeZone timeZone; + + private boolean preserveHourOfDayAcrossDaylightSavings = false; // false is backward-compatible with behavior + + private boolean skipDayIfHourDoesNotExist = false; + + private int timesTriggered = 0; + + private boolean complete = false; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Create a DateIntervalTrigger with no settings. + *

+ */ + public CalendarIntervalTriggerImpl() { + super(); + } + + /** + *

+ * Create a DateIntervalTrigger that will occur immediately, and + * repeat at the the given interval. + *

+ */ + public CalendarIntervalTriggerImpl(String name, IntervalUnit intervalUnit, int repeatInterval) { + this(name, null, intervalUnit, repeatInterval); + } + + /** + *

+ * Create a DateIntervalTrigger that will occur immediately, and + * repeat at the the given interval. + *

+ */ + public CalendarIntervalTriggerImpl(String name, String group, IntervalUnit intervalUnit, + int repeatInterval) { + this(name, group, new Date(), null, intervalUnit, repeatInterval); + } + + /** + *

+ * Create a DateIntervalTrigger that will occur at the given time, + * and repeat at the the given interval until the given end time. + *

+ * + * @param startTime + * A 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 intervalUnit + * The repeat interval unit (minutes, days, months, etc). + * @param repeatInterval + * The number of milliseconds to pause between the repeat firing. + */ + public CalendarIntervalTriggerImpl(String name, Date startTime, + Date endTime, IntervalUnit intervalUnit, int repeatInterval) { + this(name, null, startTime, endTime, intervalUnit, repeatInterval); + } + + /** + *

+ * Create a DateIntervalTrigger that will occur at the given time, + * and repeat at the the given interval until the given end time. + *

+ * + * @param startTime + * A 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 intervalUnit + * The repeat interval unit (minutes, days, months, etc). + * @param repeatInterval + * The number of milliseconds to pause between the repeat firing. + */ + public CalendarIntervalTriggerImpl(String name, String group, Date startTime, + Date endTime, IntervalUnit intervalUnit, int repeatInterval) { + super(name, group); + + setStartTime(startTime); + setEndTime(endTime); + setRepeatIntervalUnit(intervalUnit); + setRepeatInterval(repeatInterval); + } + + /** + *

+ * Create a DateIntervalTrigger that will occur at the given time, + * fire the identified Job and repeat at the the given + * interval until the given end time. + *

+ * + * @param startTime + * A 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 intervalUnit + * The repeat interval unit (minutes, days, months, etc). + * @param repeatInterval + * The number of milliseconds to pause between the repeat firing. + */ + public CalendarIntervalTriggerImpl(String name, String group, String jobName, + String jobGroup, Date startTime, Date endTime, + IntervalUnit intervalUnit, int repeatInterval) { + super(name, group, jobName, jobGroup); + + setStartTime(startTime); + setEndTime(endTime); + setRepeatIntervalUnit(intervalUnit); + setRepeatInterval(repeatInterval); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Get the time at which the DateIntervalTrigger should occur. + *

+ */ + @Override + public Date getStartTime() { + if(startTime == null) + startTime = new Date(); + return startTime; + } + + /** + *

+ * Set the time at which the DateIntervalTrigger should occur. + *

+ * + * @exception IllegalArgumentException + * if startTime is null. + */ + @Override + public void setStartTime(Date startTime) { + if (startTime == null) { + throw new IllegalArgumentException("Start time cannot be null"); + } + + Date eTime = getEndTime(); + if (eTime != null && eTime.before(startTime)) { + throw new IllegalArgumentException( + "End time cannot be before start time"); + } + + this.startTime = startTime; + } + + /** + *

+ * Get the time at which the DateIntervalTrigger should quit + * repeating. + *

+ * + * @see #getFinalFireTime() + */ + @Override + public Date getEndTime() { + return endTime; + } + + /** + *

+ * Set the time at which the DateIntervalTrigger should quit + * repeating (and be automatically deleted). + *

+ * + * @exception IllegalArgumentException + * if endTime is before start time. + */ + @Override + public void setEndTime(Date endTime) { + Date sTime = getStartTime(); + if (sTime != null && endTime != null && sTime.after(endTime)) { + throw new IllegalArgumentException( + "End time cannot be before start time"); + } + + this.endTime = endTime; + } + + /* (non-Javadoc) + * @see org.quartz.DateIntervalTriggerI#getRepeatIntervalUnit() + */ + public IntervalUnit getRepeatIntervalUnit() { + return repeatIntervalUnit; + } + + /** + *

Set the interval unit - the time unit on with the interval applies.

+ */ + public void setRepeatIntervalUnit(IntervalUnit intervalUnit) { + this.repeatIntervalUnit = intervalUnit; + } + + /* (non-Javadoc) + * @see org.quartz.DateIntervalTriggerI#getRepeatInterval() + */ + public int getRepeatInterval() { + return repeatInterval; + } + + /** + *

+ * set the the time interval that will be added to the DateIntervalTrigger's + * fire time (in the set repeat interval unit) in order to calculate the time of the + * next trigger repeat. + *

+ * + * @exception IllegalArgumentException + * if repeatInterval is < 1 + */ + public void setRepeatInterval( int repeatInterval) { + if (repeatInterval < 0) { + throw new IllegalArgumentException( + "Repeat interval must be >= 1"); + } + + this.repeatInterval = repeatInterval; + } + + /* (non-Javadoc) + * @see org.quartz.CalendarIntervalTriggerI#getTimeZone() + */ + public TimeZone getTimeZone() { + + if (timeZone == null) { + timeZone = TimeZone.getDefault(); + } + return timeZone; + } + + /** + *

+ * Sets the time zone within which time calculations related to this + * trigger will be performed. + *

+ * + * @param timeZone the desired TimeZone, or null for the system default. + */ + public void setTimeZone(TimeZone timeZone) { + this.timeZone = timeZone; + } + + /** + * If intervals are a day or greater, this property (set to true) will + * cause the firing of the trigger to always occur at the same time of day, + * (the time of day of the startTime) regardless of daylight saving time + * transitions. Default value is false. + * + *

+ * For example, without the property set, your trigger may have a start + * time of 9:00 am on March 1st, and a repeat interval of 2 days. But + * after the daylight saving transition occurs, the trigger may start + * firing at 8:00 am every other day. + *

+ * + *

+ * If however, the time of day does not exist on a given day to fire + * (e.g. 2:00 am in the United States on the days of daylight saving + * transition), the trigger will go ahead and fire one hour off on + * that day, and then resume the normal hour on other days. If + * you wish for the trigger to never fire at the "wrong" hour, then + * you should set the property skipDayIfHourDoesNotExist. + *

+ * + * @see #isSkipDayIfHourDoesNotExist() + * @see #getStartTime() + * @see #getTimeZone() + */ + public boolean isPreserveHourOfDayAcrossDaylightSavings() { + return preserveHourOfDayAcrossDaylightSavings; + } + + public void setPreserveHourOfDayAcrossDaylightSavings(boolean preserveHourOfDayAcrossDaylightSavings) { + this.preserveHourOfDayAcrossDaylightSavings = preserveHourOfDayAcrossDaylightSavings; + } + + /** + * If intervals are a day or greater, and + * preserveHourOfDayAcrossDaylightSavings property is set to true, and the + * hour of the day does not exist on a given day for which the trigger + * would fire, the day will be skipped and the trigger advanced a second + * interval if this property is set to true. Defaults to false. + * + *

+ * CAUTION! If you enable this property, and your hour of day happens + * to be that of daylight savings transition (e.g. 2:00 am in the United + * States) and the trigger's interval would have had the trigger fire on + * that day, then you may actually completely miss a firing on the day of + * transition if that hour of day does not exist on that day! In such a + * case the next fire time of the trigger will be computed as double (if + * the interval is 2 days, then a span of 4 days between firings will + * occur). + *

+ * + * @see #isPreserveHourOfDayAcrossDaylightSavings() + */ + public boolean isSkipDayIfHourDoesNotExist() { + return skipDayIfHourDoesNotExist; + } + + public void setSkipDayIfHourDoesNotExist(boolean skipDayIfHourDoesNotExist) { + this.skipDayIfHourDoesNotExist = skipDayIfHourDoesNotExist; + } + + /* (non-Javadoc) + * @see org.quartz.DateIntervalTriggerI#getTimesTriggered() + */ + public int getTimesTriggered() { + return timesTriggered; + } + + /** + *

+ * Set the number of times the DateIntervalTrigger has already + * fired. + *

+ */ + public void setTimesTriggered(int timesTriggered) { + this.timesTriggered = timesTriggered; + } + + @Override + protected boolean validateMisfireInstruction(int misfireInstruction) { + if (misfireInstruction < MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) { + return false; + } + + return misfireInstruction <= MISFIRE_INSTRUCTION_DO_NOTHING; + } + + + /** + *

+ * Updates the DateIntervalTrigger's state based on the + * MISFIRE_INSTRUCTION_XXX that was selected when the DateIntervalTrigger + * was created. + *

+ * + *

+ * If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY, + * then the following scheme will be used:
+ *

    + *
  • The instruction will be interpreted as MISFIRE_INSTRUCTION_FIRE_ONCE_NOW + *
+ *

+ */ + @Override + public void updateAfterMisfire(org.quartz.Calendar cal) { + int instr = getMisfireInstruction(); + + if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) + return; + + if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) { + instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW; + } + + if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) { + Date newFireTime = getFireTimeAfter(new Date()); + while (newFireTime != null && cal != null + && !cal.isTimeIncluded(newFireTime.getTime())) { + newFireTime = getFireTimeAfter(newFireTime); + } + setNextFireTime(newFireTime); + } else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) { + // fire once now... + setNextFireTime(new Date()); + // the new fire time afterward will magically preserve the original + // time of day for firing for day/week/month interval triggers, + // because of the way getFireTimeAfter() works - in its always restarting + // computation from the start time. + } + } + + /** + *

+ * 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). + *

+ * + * @see #executionComplete(JobExecutionContext, JobExecutionException) + */ + @Override + public void triggered(org.quartz.Calendar calendar) { + timesTriggered++; + previousFireTime = nextFireTime; + nextFireTime = getFireTimeAfter(nextFireTime); + + 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) { + nextFireTime = null; + } + } + } + + + /** + * + * @see org.quartz.spi.OperableTrigger#updateWithNewCalendar(org.quartz.Calendar, long) + */ + @Override + public void updateWithNewCalendar(org.quartz.Calendar calendar, long misfireThreshold) + { + nextFireTime = getFireTimeAfter(previousFireTime); + + if (nextFireTime == null || calendar == null) { + return; + } + + Date now = new Date(); + while (nextFireTime != 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) { + nextFireTime = null; + } + + if(nextFireTime != null && nextFireTime.before(now)) { + long diff = now.getTime() - nextFireTime.getTime(); + if(diff >= misfireThreshold) { + nextFireTime = getFireTimeAfter(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. + *

+ * + * @return the first time at which the Trigger will be fired + * by the scheduler, which is also the same value getNextFireTime() + * will return (until after the first firing of the Trigger). + *

+ */ + @Override + public Date computeFirstFireTime(org.quartz.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; + } + + /** + *

+ * 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. + *

+ */ + @Override + public Date getNextFireTime() { + return nextFireTime; + } + + /** + *

+ * Returns the previous time at which the DateIntervalTrigger + * fired. If the trigger has not yet fired, null will be + * returned. + */ + @Override + public Date getPreviousFireTime() { + return previousFireTime; + } + + /** + *

+ * Set the next time at which the DateIntervalTrigger 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 DateIntervalTrigger 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 DateIntervalTrigger will + * fire, after the given time. If the trigger will not fire after the given + * time, null will be returned. + *

+ */ + @Override + public Date getFireTimeAfter(Date afterTime) { + return getFireTimeAfter(afterTime, false); + } + + protected Date getFireTimeAfter(Date afterTime, boolean ignoreEndTime) { + if (complete) { + return null; + } + + // increment afterTme by a second, so that we are + // comparing against a time after it! + if (afterTime == null) { + afterTime = new Date(); + } + + long startMillis = getStartTime().getTime(); + long afterMillis = afterTime.getTime(); + long endMillis = (getEndTime() == null) ? Long.MAX_VALUE : getEndTime() + .getTime(); + + if (!ignoreEndTime && (endMillis <= afterMillis)) { + return null; + } + + if (afterMillis < startMillis) { + return new Date(startMillis); + } + + + long secondsAfterStart = 1 + (afterMillis - startMillis) / 1000L; + + Date time = null; + long repeatLong = getRepeatInterval(); + + Calendar aTime = Calendar.getInstance(); + aTime.setTime(afterTime); + + Calendar sTime = Calendar.getInstance(); + if(timeZone != null) + sTime.setTimeZone(timeZone); + sTime.setTime(getStartTime()); + sTime.setLenient(true); + + if(getRepeatIntervalUnit().equals(IntervalUnit.SECOND)) { + long jumpCount = secondsAfterStart / repeatLong; + if(secondsAfterStart % repeatLong != 0) + jumpCount++; + sTime.add(Calendar.SECOND, getRepeatInterval() * (int)jumpCount); + time = sTime.getTime(); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.MINUTE)) { + long jumpCount = secondsAfterStart / (repeatLong * 60L); + if(secondsAfterStart % (repeatLong * 60L) != 0) + jumpCount++; + sTime.add(Calendar.MINUTE, getRepeatInterval() * (int)jumpCount); + time = sTime.getTime(); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.HOUR)) { + long jumpCount = secondsAfterStart / (repeatLong * 60L * 60L); + if(secondsAfterStart % (repeatLong * 60L * 60L) != 0) + jumpCount++; + sTime.add(Calendar.HOUR_OF_DAY, getRepeatInterval() * (int)jumpCount); + time = sTime.getTime(); + } + else { // intervals a day or greater ... + + int initialHourOfDay = sTime.get(Calendar.HOUR_OF_DAY); + + if(getRepeatIntervalUnit().equals(IntervalUnit.DAY)) { + sTime.setLenient(true); + + // Because intervals greater than an hour have an non-fixed number + // of seconds in them (due to daylight savings, variation number of + // days in each month, leap year, etc. ) we can't jump forward an + // exact number of seconds to calculate the fire time as we can + // with the second, minute and hour intervals. But, rather + // than slowly crawling our way there by iteratively adding the + // increment to the start time until we reach the "after time", + // we can first make a big leap most of the way there... + + long jumpCount = secondsAfterStart / (repeatLong * 24L * 60L * 60L); + // if we need to make a big jump, jump most of the way there, + // but not all the way because in some cases we may over-shoot or under-shoot + if(jumpCount > 20) { + if(jumpCount < 50) + jumpCount = (long) (jumpCount * 0.80); + else if(jumpCount < 500) + jumpCount = (long) (jumpCount * 0.90); + else + jumpCount = (long) (jumpCount * 0.95); + sTime.add(java.util.Calendar.DAY_OF_YEAR, (int) (getRepeatInterval() * jumpCount)); + } + + // now baby-step the rest of the way there... + while(!sTime.getTime().after(afterTime) && + (sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) { + sTime.add(java.util.Calendar.DAY_OF_YEAR, getRepeatInterval()); + } + while(daylightSavingHourShiftOccurredAndAdvanceNeeded(sTime, initialHourOfDay, afterTime) && + (sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) { + sTime.add(java.util.Calendar.DAY_OF_YEAR, getRepeatInterval()); + } + time = sTime.getTime(); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.WEEK)) { + sTime.setLenient(true); + + // Because intervals greater than an hour have an non-fixed number + // of seconds in them (due to daylight savings, variation number of + // days in each month, leap year, etc. ) we can't jump forward an + // exact number of seconds to calculate the fire time as we can + // with the second, minute and hour intervals. But, rather + // than slowly crawling our way there by iteratively adding the + // increment to the start time until we reach the "after time", + // we can first make a big leap most of the way there... + + long jumpCount = secondsAfterStart / (repeatLong * 7L * 24L * 60L * 60L); + // if we need to make a big jump, jump most of the way there, + // but not all the way because in some cases we may over-shoot or under-shoot + if(jumpCount > 20) { + if(jumpCount < 50) + jumpCount = (long) (jumpCount * 0.80); + else if(jumpCount < 500) + jumpCount = (long) (jumpCount * 0.90); + else + jumpCount = (long) (jumpCount * 0.95); + sTime.add(java.util.Calendar.WEEK_OF_YEAR, (int) (getRepeatInterval() * jumpCount)); + } + + while(!sTime.getTime().after(afterTime) && + (sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) { + sTime.add(java.util.Calendar.WEEK_OF_YEAR, getRepeatInterval()); + } + while(daylightSavingHourShiftOccurredAndAdvanceNeeded(sTime, initialHourOfDay, afterTime) && + (sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) { + sTime.add(java.util.Calendar.WEEK_OF_YEAR, getRepeatInterval()); + } + time = sTime.getTime(); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.MONTH)) { + sTime.setLenient(true); + + // because of the large variation in size of months, and + // because months are already large blocks of time, we will + // just advance via brute-force iteration. + + while(!sTime.getTime().after(afterTime) && + (sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) { + sTime.add(java.util.Calendar.MONTH, getRepeatInterval()); + } + while(daylightSavingHourShiftOccurredAndAdvanceNeeded(sTime, initialHourOfDay, afterTime) && + (sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) { + sTime.add(java.util.Calendar.MONTH, getRepeatInterval()); + } + time = sTime.getTime(); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.YEAR)) { + + while(!sTime.getTime().after(afterTime) && + (sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) { + sTime.add(java.util.Calendar.YEAR, getRepeatInterval()); + } + while(daylightSavingHourShiftOccurredAndAdvanceNeeded(sTime, initialHourOfDay, afterTime) && + (sTime.get(java.util.Calendar.YEAR) < YEAR_TO_GIVEUP_SCHEDULING_AT)) { + sTime.add(java.util.Calendar.YEAR, getRepeatInterval()); + } + time = sTime.getTime(); + } + } // case of interval of a day or greater + + if (!ignoreEndTime && (endMillis <= time.getTime())) { + return null; + } + + return time; + } + + private boolean daylightSavingHourShiftOccurredAndAdvanceNeeded(Calendar newTime, int initialHourOfDay, Date afterTime) { + if(isPreserveHourOfDayAcrossDaylightSavings() && newTime.get(Calendar.HOUR_OF_DAY) != initialHourOfDay) { + newTime.set(Calendar.HOUR_OF_DAY, initialHourOfDay); + if (newTime.get(Calendar.HOUR_OF_DAY) != initialHourOfDay) { + return isSkipDayIfHourDoesNotExist(); + } else { + return !newTime.getTime().after(afterTime); + } + } + return false; + } + + /** + *

+ * Returns the final time at which the DateIntervalTrigger will + * fire, if there is no end time set, null will be returned. + *

+ * + *

+ * Note that the return time may be in the past. + *

+ */ + @Override + public Date getFinalFireTime() { + if (complete || getEndTime() == null) { + return null; + } + + // back up a second from end time + Date fTime = new Date(getEndTime().getTime() - 1000L); + // find the next fire time after that + fTime = getFireTimeAfter(fTime, true); + + // the the trigger fires at the end time, that's it! + if(fTime.equals(getEndTime())) + return fTime; + + // otherwise we have to back up one interval from the fire time after the end time + + Calendar lTime = Calendar.getInstance(); + if(timeZone != null) + lTime.setTimeZone(timeZone); + lTime.setTime(fTime); + lTime.setLenient(true); + + if(getRepeatIntervalUnit().equals(IntervalUnit.SECOND)) { + lTime.add(java.util.Calendar.SECOND, -1 * getRepeatInterval()); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.MINUTE)) { + lTime.add(java.util.Calendar.MINUTE, -1 * getRepeatInterval()); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.HOUR)) { + lTime.add(java.util.Calendar.HOUR_OF_DAY, -1 * getRepeatInterval()); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.DAY)) { + lTime.add(java.util.Calendar.DAY_OF_YEAR, -1 * getRepeatInterval()); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.WEEK)) { + lTime.add(java.util.Calendar.WEEK_OF_YEAR, -1 * getRepeatInterval()); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.MONTH)) { + lTime.add(java.util.Calendar.MONTH, -1 * getRepeatInterval()); + } + else if(getRepeatIntervalUnit().equals(IntervalUnit.YEAR)) { + lTime.add(java.util.Calendar.YEAR, -1 * getRepeatInterval()); + } + + return lTime.getTime(); + } + + /** + *

+ * Determines whether or not the DateIntervalTrigger will occur + * again. + *

+ */ + @Override + public boolean mayFireAgain() { + return (getNextFireTime() != null); + } + + /** + *

+ * 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. + */ + @Override + public void validate() throws SchedulerException { + super.validate(); + + if (repeatInterval < 1) { + throw new SchedulerException("Repeat Interval cannot be zero."); + } + } + + /** + * Get a {@link ScheduleBuilder} that is configured to produce a + * schedule identical to this trigger's schedule. + * + * @see #getTriggerBuilder() + */ + @Override + public ScheduleBuilder getScheduleBuilder() { + + CalendarIntervalScheduleBuilder cb = CalendarIntervalScheduleBuilder.calendarIntervalSchedule() + .withInterval(getRepeatInterval(), getRepeatIntervalUnit()); + + switch(getMisfireInstruction()) { + case MISFIRE_INSTRUCTION_DO_NOTHING : cb.withMisfireHandlingInstructionDoNothing(); + break; + case MISFIRE_INSTRUCTION_FIRE_ONCE_NOW : cb.withMisfireHandlingInstructionFireAndProceed(); + break; + } + + return cb; + } + + public boolean hasAdditionalProperties() { + return false; + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/triggers/CoreTrigger.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/triggers/CoreTrigger.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/triggers/CoreTrigger.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,29 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.impl.triggers; + +import org.quartz.Trigger; + +/** + * internal interface preserved for backward compatibility + */ +public interface CoreTrigger extends Trigger { + + public boolean hasAdditionalProperties(); + +} Index: 3rdParty_sources/quartz/org/quartz/impl/triggers/CronTriggerImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/triggers/CronTriggerImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/triggers/CronTriggerImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,847 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.triggers; + +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +import org.quartz.CronExpression; +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.ScheduleBuilder; +import org.quartz.Scheduler; +import org.quartz.Trigger; +import org.quartz.TriggerUtils; + + +/** + *

+ * A concrete {@link Trigger} that is used to fire a {@link org.quartz.JobDetail} + * at given moments in time, defined with Unix 'cron-like' definitions. + *

+ * + * + * @author Sharada Jambula, James House + * @author Contributions from Mads Henderson + */ +public class CronTriggerImpl extends AbstractTrigger implements CronTrigger, CoreTrigger { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Required for serialization support. Introduced in Quartz 1.6.1 to + * maintain compatibility after the introduction of hasAdditionalProperties + * method. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -8644953146451592766L; + + protected static final int YEAR_TO_GIVEUP_SCHEDULING_AT = CronExpression.MAX_YEAR; + + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private CronExpression cronEx = null; + private Date startTime = null; + private Date endTime = null; + private Date nextFireTime = null; + private Date previousFireTime = null; + private transient TimeZone timeZone = null; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * 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 CronTriggerImpl() { + super(); + setStartTime(new Date()); + setTimeZone(TimeZone.getDefault()); + } + + /** + *

+ * Create a CronTrigger with the given name and default 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. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public CronTriggerImpl(String name) { + this(name, null); + } + + /** + *

+ * 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. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public CronTriggerImpl(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. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public CronTriggerImpl(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 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. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public CronTriggerImpl(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 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. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public CronTriggerImpl(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 org.quartz.JobDetail}, + * and with the given "cron" expression resolved with respect to the TimeZone. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public CronTriggerImpl(String name, String group, String jobName, + String jobGroup, String cronExpression, TimeZone timeZone) + throws ParseException { + this(name, group, jobName, jobGroup, null, null, cronExpression, + 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 + * A 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. + * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public CronTriggerImpl(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 occurring 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 the Trigger + * @param group + * of the Trigger + * @param jobName + * name of the {@link org.quartz.JobDetail} + * executed on firetime + * @param jobGroup + * group of the {@link 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 interpreted, i.e. the expression 0 0 10 * * ?, is + * resolved to 10:00 am in this time zone. + * @throws ParseException + * if the cronExpression is invalid. + * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public CronTriggerImpl(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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + @Override + public Object clone() { + CronTriggerImpl copy = (CronTriggerImpl) super.clone(); + if (cronEx != null) { + copy.setCronExpression(new CronExpression(cronEx)); + } + return copy; + } + + public void setCronExpression(String cronExpression) throws ParseException { + TimeZone origTz = getTimeZone(); + this.cronEx = new CronExpression(cronExpression); + this.cronEx.setTimeZone(origTz); + } + + /* (non-Javadoc) + * @see org.quartz.CronTriggerI#getCronExpression() + */ + 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. + */ + public void setCronExpression(CronExpression cronExpression) { + this.cronEx = cronExpression; + this.timeZone = cronExpression.getTimeZone(); + } + + /** + *

+ * Get the time at which the CronTrigger should occur. + *

+ */ + @Override + public Date getStartTime() { + return this.startTime; + } + + @Override + public void setStartTime(Date startTime) { + if (startTime == null) { + throw new IllegalArgumentException("Start time cannot be null"); + } + + Date eTime = getEndTime(); + if (eTime != null && eTime.before(startTime)) { + throw new IllegalArgumentException( + "End time cannot be before start time"); + } + + // round off millisecond... + // Note timeZone is not needed here as parameter for + // Calendar.getInstance(), + // since time zone is implicit when using a Date in the setTime method. + Calendar cl = Calendar.getInstance(); + cl.setTime(startTime); + cl.set(Calendar.MILLISECOND, 0); + + this.startTime = cl.getTime(); + } + + /** + *

+ * Get the time at which the CronTrigger should quit + * repeating - even if repeastCount isn't yet satisfied. + *

+ * + * @see #getFinalFireTime() + */ + @Override + public Date getEndTime() { + return this.endTime; + } + + @Override + public void setEndTime(Date endTime) { + Date sTime = getStartTime(); + if (sTime != null && endTime != null && sTime.after(endTime)) { + throw new IllegalArgumentException( + "End time cannot be before start time"); + } + + this.endTime = endTime; + } + + /** + *

+ * 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. + *

+ * + * @see TriggerUtils#computeFireTimesBetween(org.quartz.spi.OperableTrigger, org.quartz.Calendar, java.util.Date, java.util.Date) + */ + @Override + public Date getNextFireTime() { + return this.nextFireTime; + } + + /** + *

+ * Returns the previous time at which the CronTrigger + * fired. If the trigger has not yet fired, null will be + * returned. + */ + @Override + 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. + *

+ */ + public void setNextFireTime(Date nextFireTime) { + this.nextFireTime = nextFireTime; + } + + /** + *

+ * 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; + } + + /* (non-Javadoc) + * @see org.quartz.CronTriggerI#getTimeZone() + */ + public TimeZone getTimeZone() { + + if(cronEx != null) { + return cronEx.getTimeZone(); + } + + if (timeZone == null) { + timeZone = TimeZone.getDefault(); + } + return timeZone; + } + + /** + *

+ * 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 + * org.quartz.Calendar (if any) + *

+ */ + @Override + 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 org.quartz.calendar + *

+ */ + @Override + 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. + *

+ */ + @Override + public boolean mayFireAgain() { + return (getNextFireTime() != null); + } + + @Override + protected boolean validateMisfireInstruction(int misfireInstruction) { + return misfireInstruction >= MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY && misfireInstruction <= MISFIRE_INSTRUCTION_DO_NOTHING; + } + + /** + *

+ * 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:
+ *

    + *
  • The instruction will be interpreted as MISFIRE_INSTRUCTION_FIRE_ONCE_NOW + *
+ *

+ */ + @Override + public void updateAfterMisfire(org.quartz.Calendar cal) { + int instr = getMisfireInstruction(); + + if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) + return; + + if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) { + instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW; + } + + if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) { + Date newFireTime = getFireTimeAfter(new Date()); + while (newFireTime != null && cal != null + && !cal.isTimeIncluded(newFireTime.getTime())) { + newFireTime = getFireTimeAfter(newFireTime); + } + setNextFireTime(newFireTime); + } else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) { + setNextFireTime(new Date()); + } + } + + /** + *

+ * 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). + *

+ * + * @param test the date to compare + * + * @see #willFireOn(Calendar, boolean) + */ + public boolean willFireOn(Calendar test) { + return willFireOn(test, 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 + * 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_OF_DAY, 0); + test.set(Calendar.MINUTE, 0); + test.set(Calendar.SECOND, 0); + } + + Date testTime = test.getTime(); + + Date fta = getFireTimeAfter(new Date(test.getTime().getTime() - 1000)); + + if(fta == null) + return false; + + 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); + } + + return fta.equals(testTime); + } + + /** + *

+ * 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). + *

+ * + * @see #executionComplete(JobExecutionContext, JobExecutionException) + */ + @Override + public void triggered(org.quartz.Calendar calendar) { + previousFireTime = nextFireTime; + nextFireTime = getFireTimeAfter(nextFireTime); + + while (nextFireTime != null && calendar != null + && !calendar.isTimeIncluded(nextFireTime.getTime())) { + nextFireTime = getFireTimeAfter(nextFireTime); + } + } + + /** + * + * @see AbstractTrigger#updateWithNewCalendar(org.quartz.Calendar, long) + */ + @Override + public void updateWithNewCalendar(org.quartz.Calendar calendar, long misfireThreshold) + { + nextFireTime = getFireTimeAfter(previousFireTime); + + if (nextFireTime == null || calendar == null) { + return; + } + + Date now = new Date(); + while (nextFireTime != null && !calendar.isTimeIncluded(nextFireTime.getTime())) { + + nextFireTime = getFireTimeAfter(nextFireTime); + + if(nextFireTime == null) + break; + + //avoid infinite loop + // Use gregorian only because the constant is based on Gregorian + java.util.Calendar c = new java.util.GregorianCalendar(); + c.setTime(nextFireTime); + if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) { + nextFireTime = null; + } + + if(nextFireTime != null && nextFireTime.before(now)) { + long diff = now.getTime() - nextFireTime.getTime(); + if(diff >= misfireThreshold) { + nextFireTime = getFireTimeAfter(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. + *

+ * + * @return the first time at which the Trigger will be fired + * by the scheduler, which is also the same value getNextFireTime() + * will return (until after the first firing of the Trigger). + *

+ */ + @Override + public Date computeFirstFireTime(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; + } + + /* (non-Javadoc) + * @see org.quartz.CronTriggerI#getExpressionSummary() + */ + 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; + } + /** + * Get a {@link ScheduleBuilder} that is configured to produce a + * schedule identical to this trigger's schedule. + * + * @see #getTriggerBuilder() + */ + @Override + public ScheduleBuilder getScheduleBuilder() { + + CronScheduleBuilder cb = CronScheduleBuilder.cronSchedule(getCronExpression()) + .inTimeZone(getTimeZone()); + + switch(getMisfireInstruction()) { + case MISFIRE_INSTRUCTION_DO_NOTHING : cb.withMisfireHandlingInstructionDoNothing(); + break; + case MISFIRE_INSTRUCTION_FIRE_ONCE_NOW : cb.withMisfireHandlingInstructionFireAndProceed(); + break; + } + + return cb; + } + + //////////////////////////////////////////////////////////////////////////// + // + // 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 eTime) { + return (cronEx == null) ? null : cronEx.getTimeBefore(eTime); + } + + +} + Index: 3rdParty_sources/quartz/org/quartz/impl/triggers/DailyTimeIntervalTriggerImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/triggers/DailyTimeIntervalTriggerImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/triggers/DailyTimeIntervalTriggerImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,975 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.triggers; + +import java.util.Calendar; +import java.util.Date; +import java.util.Set; + +import org.quartz.DailyTimeIntervalScheduleBuilder; +import org.quartz.DailyTimeIntervalTrigger; +import org.quartz.DateBuilder; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.ScheduleBuilder; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TimeOfDay; +import org.quartz.Trigger; +import org.quartz.DateBuilder.IntervalUnit; + +/** + * A concrete implementation of DailyTimeIntervalTrigger that is used to fire a {@link org.quartz.JobDetail} + * based upon daily repeating time intervals. + * + *

The trigger will fire every N (see {@link #setRepeatInterval(int)} ) seconds, minutes or hours + * (see {@link #setRepeatIntervalUnit(org.quartz.DateBuilder.IntervalUnit)}) during a given time window on specified days of the week.

+ * + *

For example#1, a trigger can be set to fire every 72 minutes between 8:00 and 11:00 everyday. It's fire times would + * be 8:00, 9:12, 10:24, then next day would repeat: 8:00, 9:12, 10:24 again.

+ * + *

For example#2, a trigger can be set to fire every 23 minutes between 9:20 and 16:47 Monday through Friday.

+ * + *

On each day, the starting fire time is reset to startTimeOfDay value, and then it will add repeatInterval value to it until + * the endTimeOfDay is reached. If you set daysOfWeek values, then fire time will only occur during those week days period. Again, + * remember this trigger will reset fire time each day with startTimeOfDay, regardless of your interval or endTimeOfDay!

+ * + *

The default values for fields if not set are: startTimeOfDay defaults to 00:00:00, the endTimeOfDay default to 23:59:59, + * and daysOfWeek is default to every day. The startTime default to current time-stamp now, while endTime has not value.

+ * + *

If startTime is before startTimeOfDay, then startTimeOfDay will be used and startTime has no affect other than to specify + * the first day of firing. Else if startTime is + * after startTimeOfDay, then the first fire time for that day will be the next interval after the startTime. For example, if + * you set startingTimeOfDay=9am, endingTimeOfDay=11am, interval=15 mins, and startTime=9:33am, then the next fire time will + * be 9:45pm. Note also that if you do not set startTime value, the trigger builder will default to current time, and current time + * maybe before or after the startTimeOfDay! So be aware how you set your startTime.

+ * + *

This trigger also supports "repeatCount" feature to end the trigger fire time after + * a certain number of count is reached. Just as the SimpleTrigger, setting repeatCount=0 + * means trigger will fire once only! Setting any positive count then the trigger will repeat + * count + 1 times. Unlike SimpleTrigger, the default value of repeatCount of this trigger + * is set to REPEAT_INDEFINITELY instead of 0 though. + * + * @see DailyTimeIntervalTrigger + * @see DailyTimeIntervalScheduleBuilder + * + * @since 2.1.0 + * + * @author James House + * @author Zemian Deng + */ +public class DailyTimeIntervalTriggerImpl extends AbstractTrigger implements DailyTimeIntervalTrigger, CoreTrigger { + + private static final long serialVersionUID = -632667786771388749L; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private Date startTime = null; + + private Date endTime = null; + + private Date nextFireTime = null; + + private Date previousFireTime = null; + + private int repeatCount = REPEAT_INDEFINITELY; + + private int repeatInterval = 1; + + private IntervalUnit repeatIntervalUnit = IntervalUnit.MINUTE; + + private Set daysOfWeek; + + private TimeOfDay startTimeOfDay; + + private TimeOfDay endTimeOfDay; + + private int timesTriggered = 0; + + private boolean complete = false; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Create a DailyTimeIntervalTrigger with no settings. + *

+ */ + public DailyTimeIntervalTriggerImpl() { + super(); + } + + /** + *

+ * Create a DailyTimeIntervalTrigger that will occur immediately, and + * repeat at the the given interval. + *

+ * + * @param startTimeOfDay + * The TimeOfDay that the repeating should begin occurring. + * @param endTimeOfDay + * The TimeOfDay that the repeating should stop occurring. + * @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are + * {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}. + * @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less. + */ + public DailyTimeIntervalTriggerImpl(String name, TimeOfDay startTimeOfDay, TimeOfDay endTimeOfDay, IntervalUnit intervalUnit, int repeatInterval) { + this(name, null, startTimeOfDay, endTimeOfDay, intervalUnit, repeatInterval); + } + + /** + *

+ * Create a DailyTimeIntervalTrigger that will occur immediately, and + * repeat at the the given interval. + *

+ * + * @param startTimeOfDay + * The TimeOfDay that the repeating should begin occurring. + * @param endTimeOfDay + * The TimeOfDay that the repeating should stop occurring. + * @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are + * {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}. + * @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less. + */ + public DailyTimeIntervalTriggerImpl(String name, String group, TimeOfDay startTimeOfDay, + TimeOfDay endTimeOfDay, IntervalUnit intervalUnit, int repeatInterval) { + this(name, group, new Date(), null, startTimeOfDay, endTimeOfDay, intervalUnit, repeatInterval); + } + + /** + *

+ * Create a DailyTimeIntervalTrigger that will occur at the given time, + * and repeat at the the given interval until the given end time. + *

+ * + * @param startTime + * A 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 startTimeOfDay + * The TimeOfDay that the repeating should begin occurring. + * @param endTimeOfDay + * The TimeOfDay that the repeating should stop occurring. + * @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are + * {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}. + * @param repeatInterval + * The number of milliseconds to pause between the repeat firing. + * @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less. + */ + public DailyTimeIntervalTriggerImpl(String name, Date startTime, + Date endTime, TimeOfDay startTimeOfDay, TimeOfDay endTimeOfDay, + IntervalUnit intervalUnit, int repeatInterval) { + this(name, null, startTime, endTime, startTimeOfDay, endTimeOfDay, intervalUnit, repeatInterval); + } + + /** + *

+ * Create a DailyTimeIntervalTrigger that will occur at the given time, + * and repeat at the the given interval until the given end time. + *

+ * + * @param startTime + * A 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 startTimeOfDay + * The TimeOfDay that the repeating should begin occurring. + * @param endTimeOfDay + * The TimeOfDay that the repeating should stop occurring. + * @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are + * {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}. + * @param repeatInterval + * The number of milliseconds to pause between the repeat firing. + * @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less. + */ + public DailyTimeIntervalTriggerImpl(String name, String group, Date startTime, + Date endTime, TimeOfDay startTimeOfDay, TimeOfDay endTimeOfDay, + IntervalUnit intervalUnit, int repeatInterval) { + super(name, group); + + setStartTime(startTime); + setEndTime(endTime); + setRepeatIntervalUnit(intervalUnit); + setRepeatInterval(repeatInterval); + setStartTimeOfDay(startTimeOfDay); + setEndTimeOfDay(endTimeOfDay); + } + + /** + *

+ * Create a DailyTimeIntervalTrigger that will occur at the given time, + * fire the identified Job and repeat at the the given + * interval until the given end time. + *

+ * + * @param startTime + * A 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 startTimeOfDay + * The TimeOfDay that the repeating should begin occurring. + * @param endTimeOfDay + * The TimeOfDay that the repeating should stop occurring. + * @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are + * {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}. + * @param repeatInterval + * The number of milliseconds to pause between the repeat firing. + * @throws IllegalArgumentException if an invalid IntervalUnit is given, or the repeat interval is zero or less. + */ + public DailyTimeIntervalTriggerImpl(String name, String group, String jobName, + String jobGroup, Date startTime, Date endTime, + TimeOfDay startTimeOfDay, TimeOfDay endTimeOfDay, + IntervalUnit intervalUnit, int repeatInterval) { + super(name, group, jobName, jobGroup); + + setStartTime(startTime); + setEndTime(endTime); + setRepeatIntervalUnit(intervalUnit); + setRepeatInterval(repeatInterval); + setStartTimeOfDay(startTimeOfDay); + setEndTimeOfDay(endTimeOfDay); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Get the time at which the DailyTimeIntervalTrigger should occur. It defaults to + * the getStartTimeOfDay of current day. + *

+ */ + @Override + public Date getStartTime() { + if(startTime == null) { + startTime = new Date(); + } + return startTime; + } + + /** + *

+ * Set the time at which the DailyTimeIntervalTrigger should occur. + *

+ * + * @exception IllegalArgumentException + * if startTime is null. + */ + @Override + public void setStartTime(Date startTime) { + if (startTime == null) { + throw new IllegalArgumentException("Start time cannot be null"); + } + + Date eTime = getEndTime(); + if (eTime != null && eTime.before(startTime)) { + throw new IllegalArgumentException( + "End time cannot be before start time"); + } + + this.startTime = startTime; + } + + /** + *

+ * Get the time at which the DailyTimeIntervalTrigger should quit + * repeating. + *

+ * + * @see #getFinalFireTime() + */ + @Override + public Date getEndTime() { + return endTime; + } + + /** + *

+ * Set the time at which the DailyTimeIntervalTrigger should quit + * repeating (and be automatically deleted). + *

+ * + * @exception IllegalArgumentException + * if endTime is before start time. + */ + @Override + public void setEndTime(Date endTime) { + Date sTime = getStartTime(); + if (sTime != null && endTime != null && sTime.after(endTime)) { + throw new IllegalArgumentException( + "End time cannot be before start time"); + } + + this.endTime = endTime; + } + + /* (non-Javadoc) + * @see org.quartz.DailyTimeIntervalTriggerI#getRepeatIntervalUnit() + */ + public IntervalUnit getRepeatIntervalUnit() { + return repeatIntervalUnit; + } + + /** + *

Set the interval unit - the time unit on with the interval applies.

+ * + * @param intervalUnit The repeat interval unit. The only intervals that are valid for this type of trigger are + * {@link IntervalUnit#SECOND}, {@link IntervalUnit#MINUTE}, and {@link IntervalUnit#HOUR}. + */ + public void setRepeatIntervalUnit(IntervalUnit intervalUnit) { + if (repeatIntervalUnit == null || + !((repeatIntervalUnit.equals(IntervalUnit.SECOND) || + repeatIntervalUnit.equals(IntervalUnit.MINUTE) || + repeatIntervalUnit.equals(IntervalUnit.HOUR)))) + throw new IllegalArgumentException("Invalid repeat IntervalUnit (must be SECOND, MINUTE or HOUR)."); + this.repeatIntervalUnit = intervalUnit; + } + + /* (non-Javadoc) + * @see org.quartz.DailyTimeIntervalTriggerI#getRepeatInterval() + */ + public int getRepeatInterval() { + return repeatInterval; + } + + /** + *

+ * set the the time interval that will be added to the DailyTimeIntervalTrigger's + * fire time (in the set repeat interval unit) in order to calculate the time of the + * next trigger repeat. + *

+ * + * @exception IllegalArgumentException + * if repeatInterval is < 1 + */ + public void setRepeatInterval( int repeatInterval) { + if (repeatInterval < 0) { + throw new IllegalArgumentException( + "Repeat interval must be >= 1"); + } + + this.repeatInterval = repeatInterval; + } + + /* (non-Javadoc) + * @see org.quartz.DailyTimeIntervalTriggerI#getTimesTriggered() + */ + public int getTimesTriggered() { + return timesTriggered; + } + + /** + *

+ * Set the number of times the DailyTimeIntervalTrigger has already + * fired. + *

+ */ + public void setTimesTriggered(int timesTriggered) { + this.timesTriggered = timesTriggered; + } + + @Override + protected boolean validateMisfireInstruction(int misfireInstruction) { + return misfireInstruction >= MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY && misfireInstruction <= MISFIRE_INSTRUCTION_DO_NOTHING; + + } + + + /** + *

+ * Updates the DailyTimeIntervalTrigger's state based on the + * MISFIRE_INSTRUCTION_XXX that was selected when the DailyTimeIntervalTrigger + * was created. + *

+ * + *

+ * If the misfire instruction is set to MISFIRE_INSTRUCTION_SMART_POLICY, + * then the following scheme will be used:
+ *

    + *
  • The instruction will be interpreted as MISFIRE_INSTRUCTION_FIRE_ONCE_NOW + *
+ *

+ */ + @Override + public void updateAfterMisfire(org.quartz.Calendar cal) { + int instr = getMisfireInstruction(); + + if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) + return; + + if (instr == MISFIRE_INSTRUCTION_SMART_POLICY) { + instr = MISFIRE_INSTRUCTION_FIRE_ONCE_NOW; + } + + if (instr == MISFIRE_INSTRUCTION_DO_NOTHING) { + Date newFireTime = getFireTimeAfter(new Date()); + while (newFireTime != null && cal != null + && !cal.isTimeIncluded(newFireTime.getTime())) { + newFireTime = getFireTimeAfter(newFireTime); + } + setNextFireTime(newFireTime); + } else if (instr == MISFIRE_INSTRUCTION_FIRE_ONCE_NOW) { + // fire once now... + setNextFireTime(new Date()); + // the new fire time afterward will magically preserve the original + // time of day for firing for day/week/month interval triggers, + // because of the way getFireTimeAfter() works - in its always restarting + // computation from the start time. + } + } + + /** + *

+ * 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). + *

+ * + * @see #executionComplete(JobExecutionContext, JobExecutionException) + */ + @Override + public void triggered(org.quartz.Calendar calendar) { + timesTriggered++; + previousFireTime = nextFireTime; + nextFireTime = getFireTimeAfter(nextFireTime); + + 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) { + nextFireTime = null; + } + } + + if (nextFireTime == null) { + complete = true; + } + } + + + /** + * @see org.quartz.impl.triggers.AbstractTrigger#updateWithNewCalendar(org.quartz.Calendar, long) + */ + @Override + public void updateWithNewCalendar(org.quartz.Calendar calendar, long misfireThreshold) + { + nextFireTime = getFireTimeAfter(previousFireTime); + + if (nextFireTime == null || calendar == null) { + return; + } + + Date now = new Date(); + while (nextFireTime != 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) { + nextFireTime = null; + } + + if(nextFireTime != null && nextFireTime.before(now)) { + long diff = now.getTime() - nextFireTime.getTime(); + if(diff >= misfireThreshold) { + nextFireTime = getFireTimeAfter(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. + *

+ * + * @return the first time at which the Trigger will be fired + * by the scheduler, which is also the same value getNextFireTime() + * will return (until after the first firing of the Trigger). + *

+ */ + @Override + public Date computeFirstFireTime(org.quartz.Calendar calendar) { + Date sTime = getStartTime(); + Date startTimeOfDayDate = getStartTimeOfDay().getTimeOfDayForDate(sTime); + + if(DateBuilder.evenSecondDate(startTime).equals(startTimeOfDayDate)) { + return startTime; + } + else if (sTime.after(startTimeOfDayDate)) { + // If startTime is after the timeOfDay, then look for the next time + nextFireTime = getFireTimeAfter(sTime); + } else { + // If startTime is before the timeOfDay then advance to timeOfDay (and if necessary dayOfWeek) + nextFireTime = advanceToNextDayOfWeekIfNecessary(startTimeOfDayDate, false); + } + + // Check calendar for date-time exclusion + 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; + } + + private Calendar createCalendarTime(Date dateTime) { + Calendar cal = Calendar.getInstance(); + cal.setTime(dateTime); + return cal; + } + + /** + *

+ * 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. + *

+ */ + @Override + public Date getNextFireTime() { + return nextFireTime; + } + + /** + *

+ * Returns the previous time at which the DailyTimeIntervalTrigger + * fired. If the trigger has not yet fired, null will be + * returned. + */ + @Override + public Date getPreviousFireTime() { + return previousFireTime; + } + + /** + *

+ * Set the next time at which the DailyTimeIntervalTrigger 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 DailyTimeIntervalTrigger 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 DailyTimeIntervalTrigger will + * fire, after the given time. If the trigger will not fire after the given + * time, null will be returned. + *

+ */ + @Override + public Date getFireTimeAfter(Date afterTime) { + // Check if trigger has completed or not. + if (complete) { + return null; + } + + // Check repeatCount limit + if (repeatCount != REPEAT_INDEFINITELY && timesTriggered > repeatCount) { + return null; + } + + // a. Increment afterTime by a second, so that we are comparing against a time after it! + if (afterTime == null) { + afterTime = new Date(System.currentTimeMillis() + 1000L); + } else { + afterTime = new Date(afterTime.getTime() + 1000L); + } + + // make sure afterTime is at least startTime + if(afterTime.before(startTime)) + afterTime = startTime; + + // b.Check to see if afterTime is after endTimeOfDay or not. If yes, then we need to advance to next day as well. + boolean afterTimePastEndTimeOfDay = false; + if (endTimeOfDay != null) { + afterTimePastEndTimeOfDay = afterTime.getTime() > endTimeOfDay.getTimeOfDayForDate(afterTime).getTime(); + } + // c. now we need to move move to the next valid day of week if either: + // the given time is past the end time of day, or given time is not on a valid day of week + Date fireTime = advanceToNextDayOfWeekIfNecessary(afterTime, afterTimePastEndTimeOfDay); + if (fireTime == null) + return null; + + // d. Calculate and save fireTimeEndDate variable for later use + Date fireTimeEndDate = null; + if (endTimeOfDay == null) + fireTimeEndDate = new TimeOfDay(23, 59, 59).getTimeOfDayForDate(fireTime); + else + fireTimeEndDate = endTimeOfDay.getTimeOfDayForDate(fireTime); + + // e. Check fireTime against startTime or startTimeOfDay to see which go first. + Date fireTimeStartDate = startTimeOfDay.getTimeOfDayForDate(fireTime); + if (fireTime.before(fireTimeStartDate)) { + return fireTimeStartDate; + } + + + // f. Continue to calculate the fireTime by incremental unit of intervals. + // recall that if fireTime was less that fireTimeStartDate, we didn't get this far + long fireMillis = fireTime.getTime(); + long startMillis = fireTimeStartDate.getTime(); + long secondsAfterStart = (fireMillis - startMillis) / 1000L; + long repeatLong = getRepeatInterval(); + Calendar sTime = createCalendarTime(fireTimeStartDate); + IntervalUnit repeatUnit = getRepeatIntervalUnit(); + if(repeatUnit.equals(IntervalUnit.SECOND)) { + long jumpCount = secondsAfterStart / repeatLong; + if(secondsAfterStart % repeatLong != 0) + jumpCount++; + sTime.add(Calendar.SECOND, getRepeatInterval() * (int)jumpCount); + fireTime = sTime.getTime(); + } else if(repeatUnit.equals(IntervalUnit.MINUTE)) { + long jumpCount = secondsAfterStart / (repeatLong * 60L); + if(secondsAfterStart % (repeatLong * 60L) != 0) + jumpCount++; + sTime.add(Calendar.MINUTE, getRepeatInterval() * (int)jumpCount); + fireTime = sTime.getTime(); + } else if(repeatUnit.equals(IntervalUnit.HOUR)) { + long jumpCount = secondsAfterStart / (repeatLong * 60L * 60L); + if(secondsAfterStart % (repeatLong * 60L * 60L) != 0) + jumpCount++; + sTime.add(Calendar.HOUR_OF_DAY, getRepeatInterval() * (int)jumpCount); + fireTime = sTime.getTime(); + } + + // g. Ensure this new fireTime is within the day, or else we need to advance to next day. + if (fireTime.after(fireTimeEndDate)) { + fireTime = advanceToNextDayOfWeekIfNecessary(fireTime, isSameDay(fireTime, fireTimeEndDate)); + // make sure we hit the startTimeOfDay on the new day + fireTime = startTimeOfDay.getTimeOfDayForDate(fireTime); + } + + // i. Return calculated fireTime. + return fireTime; + } + + private boolean isSameDay(Date d1, Date d2) { + + Calendar c1 = createCalendarTime(d1); + Calendar c2 = createCalendarTime(d2); + + return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) && c1.get(Calendar.DAY_OF_YEAR) == c2.get(Calendar.DAY_OF_YEAR); + } + + /** + * Given fireTime time determine if it is on a valid day of week. If so, simply return it unaltered, + * if not, advance to the next valid week day, and set the time of day to the start time of day + * + * @param fireTime - given next fireTime. + * @param forceToAdvanceNextDay - flag to whether to advance day without check existing week day. This scenario + * can happen when a caller determine fireTime has passed the endTimeOfDay that fireTime should move to next day anyway. + * @return a next day fireTime. + */ + private Date advanceToNextDayOfWeekIfNecessary(Date fireTime, boolean forceToAdvanceNextDay) { + // a. Advance or adjust to next dayOfWeek if need to first, starting next day with startTimeOfDay. + TimeOfDay sTimeOfDay = getStartTimeOfDay(); + Date fireTimeStartDate = sTimeOfDay.getTimeOfDayForDate(fireTime); + Calendar fireTimeStartDateCal = createCalendarTime(fireTimeStartDate); + int dayOfWeekOfFireTime = fireTimeStartDateCal.get(Calendar.DAY_OF_WEEK); + + // b2. We need to advance to another day if isAfterTimePassEndTimeOfDay is true, or dayOfWeek is not set. + Set daysOfWeekToFire = getDaysOfWeek(); + if (forceToAdvanceNextDay || !daysOfWeekToFire.contains(dayOfWeekOfFireTime)) { + // Advance one day at a time until next available date. + for(int i=1; i <= 7; i++) { + fireTimeStartDateCal.add(Calendar.DATE, 1); + dayOfWeekOfFireTime = fireTimeStartDateCal.get(Calendar.DAY_OF_WEEK); + if (daysOfWeekToFire.contains(dayOfWeekOfFireTime)) { + fireTime = fireTimeStartDateCal.getTime(); + break; + } + } + } + + // Check fireTime not pass the endTime + Date eTime = getEndTime(); + if (eTime != null && fireTime.getTime() > eTime.getTime()) { + return null; + } + + return fireTime; + } + + /** + *

+ * Returns the final time at which the DailyTimeIntervalTrigger will + * fire, if there is no end time set, null will be returned. + *

+ * + *

+ * Note that the return time may be in the past. + *

+ */ + @Override + public Date getFinalFireTime() { + if (complete || getEndTime() == null) { + return null; + } + + // We have an endTime, we still need to check to see if there is a endTimeOfDay if that's applicable. + Date eTime = getEndTime(); + if (endTimeOfDay != null) { + Date endTimeOfDayDate = endTimeOfDay.getTimeOfDayForDate(eTime); + if (eTime.getTime() < endTimeOfDayDate.getTime()) { + eTime = endTimeOfDayDate; + } + } + return eTime; + } + + /** + *

+ * Determines whether or not the DailyTimeIntervalTrigger will occur + * again. + *

+ */ + @Override + public boolean mayFireAgain() { + return (getNextFireTime() != null); + } + + /** + *

+ * 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. + */ + @Override + public void validate() throws SchedulerException { + super.validate(); + + if (repeatIntervalUnit == null || !(repeatIntervalUnit.equals(IntervalUnit.SECOND) || + repeatIntervalUnit.equals(IntervalUnit.MINUTE) ||repeatIntervalUnit.equals(IntervalUnit.HOUR))) + throw new SchedulerException("Invalid repeat IntervalUnit (must be SECOND, MINUTE or HOUR)."); + if (repeatInterval < 1) { + throw new SchedulerException("Repeat Interval cannot be zero."); + } + + // Ensure interval does not exceed 24 hours + long secondsInHour = 24 * 60 * 60L; + if (repeatIntervalUnit == IntervalUnit.SECOND && repeatInterval > secondsInHour) { + throw new SchedulerException("repeatInterval can not exceed 24 hours (" + secondsInHour + " seconds). Given " + repeatInterval); + } + if (repeatIntervalUnit == IntervalUnit.MINUTE && repeatInterval > secondsInHour / 60L) { + throw new SchedulerException("repeatInterval can not exceed 24 hours (" + secondsInHour / 60L + " minutes). Given " + repeatInterval); + } + if (repeatIntervalUnit == IntervalUnit.HOUR && repeatInterval > 24 ) { + throw new SchedulerException("repeatInterval can not exceed 24 hours. Given " + repeatInterval + " hours."); + } + + // Ensure timeOfDay is in order. + if (getEndTimeOfDay() != null && !getStartTimeOfDay().before(getEndTimeOfDay())) { + throw new SchedulerException("StartTimeOfDay " + startTimeOfDay + " should not come after endTimeOfDay " + endTimeOfDay); + } + } + + /** + * {@inheritDoc} + */ + public Set getDaysOfWeek() { + if (daysOfWeek == null) { + daysOfWeek = DailyTimeIntervalScheduleBuilder.ALL_DAYS_OF_THE_WEEK; + } + return daysOfWeek; + } + + public void setDaysOfWeek(Set daysOfWeek) { + if(daysOfWeek == null || daysOfWeek.size() == 0) + throw new IllegalArgumentException("DaysOfWeek set must be a set that contains at least one day."); + else if(daysOfWeek.size() == 0) + throw new IllegalArgumentException("DaysOfWeek set must contain at least one day."); + + this.daysOfWeek = daysOfWeek; + } + + /** + * {@inheritDoc} + */ + public TimeOfDay getStartTimeOfDay() { + if (startTimeOfDay == null) { + startTimeOfDay = new TimeOfDay(0, 0, 0); + } + return startTimeOfDay; + } + + public void setStartTimeOfDay(TimeOfDay startTimeOfDay) { + if (startTimeOfDay == null) { + throw new IllegalArgumentException("Start time of day cannot be null"); + } + + TimeOfDay eTime = getEndTimeOfDay(); + if (eTime != null && eTime.before(startTimeOfDay)) { + throw new IllegalArgumentException( + "End time of day cannot be before start time of day"); + } + + this.startTimeOfDay = startTimeOfDay; + } + + /** + * {@inheritDoc} + */ + public TimeOfDay getEndTimeOfDay() { + return endTimeOfDay; + } + + public void setEndTimeOfDay(TimeOfDay endTimeOfDay) { + if (endTimeOfDay == null) + throw new IllegalArgumentException("End time of day cannot be null"); + + TimeOfDay sTime = getStartTimeOfDay(); + if (sTime != null && endTimeOfDay.before(endTimeOfDay)) { + throw new IllegalArgumentException( + "End time of day cannot be before start time of day"); + } + this.endTimeOfDay = endTimeOfDay; + } + + /** + * Get a {@link ScheduleBuilder} that is configured to produce a + * schedule identical to this trigger's schedule. + * + * @see #getTriggerBuilder() + */ + @Override + public ScheduleBuilder getScheduleBuilder() { + + DailyTimeIntervalScheduleBuilder cb = DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule() + .withInterval(getRepeatInterval(), getRepeatIntervalUnit()) + .onDaysOfTheWeek(getDaysOfWeek()).startingDailyAt(getStartTimeOfDay()).endingDailyAt(getEndTimeOfDay()); + + switch(getMisfireInstruction()) { + case MISFIRE_INSTRUCTION_DO_NOTHING : cb.withMisfireHandlingInstructionDoNothing(); + break; + case MISFIRE_INSTRUCTION_FIRE_ONCE_NOW : cb.withMisfireHandlingInstructionFireAndProceed(); + break; + } + + return cb; + } + + /** This trigger has no additional properties besides what's defined in this class. */ + public boolean hasAdditionalProperties() { + return false; + } + + public int getRepeatCount() { + return repeatCount; + } + + public void setRepeatCount(int repeatCount) { + if (repeatCount < 0 && repeatCount != REPEAT_INDEFINITELY) { + throw new IllegalArgumentException("Repeat count must be >= 0, use the " + + "constant REPEAT_INDEFINITELY for infinite."); + } + + this.repeatCount = repeatCount; + } +} Index: 3rdParty_sources/quartz/org/quartz/impl/triggers/SimpleTriggerImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/triggers/SimpleTriggerImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/triggers/SimpleTriggerImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,912 @@ + +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.impl.triggers; + +import java.util.Date; + +import org.quartz.Calendar; +import org.quartz.CronTrigger; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.ScheduleBuilder; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SimpleScheduleBuilder; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.quartz.TriggerUtils; + + +/** + *

+ * A concrete {@link Trigger} that is used to fire a {@link org.quartz.JobDetail} + * at a given moment in time, and optionally repeated at a specified interval. + *

+ * + * @see Trigger + * @see CronTrigger + * @see TriggerUtils + * + * @author James House + * @author contributions by Lieven Govaerts of Ebitec Nv, Belgium. + */ +public class SimpleTriggerImpl extends AbstractTrigger implements SimpleTrigger, CoreTrigger { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Required for serialization support. Introduced in Quartz 1.6.1 to + * maintain compatibility after the introduction of hasAdditionalProperties + * method. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -3735980074222850397L; + + private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + *

+ */ + public SimpleTriggerImpl() { + super(); + } + + /** + *

+ * Create a SimpleTrigger that will occur immediately, and + * not repeat. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public SimpleTriggerImpl(String name) { + this(name, (String)null); + } + + /** + *

+ * Create a SimpleTrigger that will occur immediately, and + * not repeat. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public SimpleTriggerImpl(String name, String group) { + this(name, group, new Date(), null, 0, 0); + } + + /** + *

+ * Create a SimpleTrigger that will occur immediately, and + * repeat at the the given interval the given number of times. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public SimpleTriggerImpl(String name, int repeatCount, long repeatInterval) { + this(name, null, repeatCount, repeatInterval); + } + + /** + *

+ * Create a SimpleTrigger that will occur immediately, and + * repeat at the the given interval the given number of times. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public SimpleTriggerImpl(String name, String group, int repeatCount, + long repeatInterval) { + this(name, group, new Date(), null, repeatCount, repeatInterval); + } + + /** + *

+ * Create a SimpleTrigger that will occur at the given time, + * and not repeat. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public SimpleTriggerImpl(String name, Date startTime) { + this(name, null, startTime); + } + + /** + *

+ * Create a SimpleTrigger that will occur at the given time, + * and not repeat. + *

+ * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public SimpleTriggerImpl(String name, String group, Date startTime) { + this(name, group, startTime, null, 0, 0); + } + + /** + *

+ * 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. + *

+ * + * @param startTime + * A 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. + * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public SimpleTriggerImpl(String name, Date startTime, + Date endTime, int repeatCount, long repeatInterval) { + this(name, null, startTime, endTime, repeatCount, repeatInterval); + } + + /** + *

+ * 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. + *

+ * + * @param startTime + * A 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. + * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public SimpleTriggerImpl(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. + *

+ * + * @param startTime + * A 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. + * + * @deprecated use a TriggerBuilder instead + */ + @Deprecated + public SimpleTriggerImpl(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. + *

+ */ + @Override + public Date getStartTime() { + return startTime; + } + + /** + *

+ * Set the time at which the SimpleTrigger should occur. + *

+ * + * @exception IllegalArgumentException + * if startTime is null. + */ + @Override + 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. + *

+ * + * @see #getFinalFireTime() + */ + @Override + public Date getEndTime() { + return endTime; + } + + /** + *

+ * Set the time at which the SimpleTrigger should quit + * repeating (and be automatically deleted). + *

+ * + * @exception IllegalArgumentException + * if endTime is before start time. + */ + @Override + public void setEndTime(Date endTime) { + Date sTime = getStartTime(); + if (sTime != null && endTime != null && sTime.after(endTime)) { + throw new IllegalArgumentException( + "End time cannot be before start time"); + } + + this.endTime = endTime; + } + + /* (non-Javadoc) + * @see org.quartz.SimpleTriggerI#getRepeatCount() + */ + public int getRepeatCount() { + return repeatCount; + } + + /** + *

+ * Set the the number of time the SimpleTrigger should + * repeat, after which it will be automatically deleted. + *

+ * + * @see #REPEAT_INDEFINITELY + * @exception IllegalArgumentException + * if repeatCount is < 0 + */ + public void setRepeatCount(int repeatCount) { + if (repeatCount < 0 && repeatCount != REPEAT_INDEFINITELY) { + throw new IllegalArgumentException( + "Repeat count must be >= 0, use the " + + "constant REPEAT_INDEFINITELY for infinite."); + } + + this.repeatCount = repeatCount; + } + + /* (non-Javadoc) + * @see org.quartz.SimpleTriggerI#getRepeatInterval() + */ + public long getRepeatInterval() { + return repeatInterval; + } + + /** + *

+ * Set the the time interval (in milliseconds) at which the SimpleTrigger + * should repeat. + *

+ * + * @exception IllegalArgumentException + * if repeatInterval is <= 0 + */ + public void setRepeatInterval(long repeatInterval) { + if (repeatInterval < 0) { + throw new IllegalArgumentException( + "Repeat interval must be >= 0"); + } + + this.repeatInterval = repeatInterval; + } + + /** + *

+ * Get the number of times the SimpleTrigger has already + * fired. + *

+ */ + public int getTimesTriggered() { + return timesTriggered; + } + + /** + *

+ * Set the number of times the SimpleTrigger has already + * fired. + *

+ */ + public void setTimesTriggered(int timesTriggered) { + this.timesTriggered = timesTriggered; + } + + @Override + protected boolean validateMisfireInstruction(int misfireInstruction) { + if (misfireInstruction < MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) { + return false; + } + + if (misfireInstruction > MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) { + return false; + } + + return true; + } + + /** + *

+ * 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:
+ *

    + *
  • If the Repeat Count is 0, then the instruction will + * be interpreted as MISFIRE_INSTRUCTION_FIRE_NOW.
  • + *
  • If the Repeat Count is 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. + *
  • + *
  • If the Repeat Count is > 0, then the instruction + * will be interpreted as MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT. + *
  • + *
+ *

+ */ + @Override + public void updateAfterMisfire(Calendar cal) { + int instr = getMisfireInstruction(); + + if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) + return; + + if (instr == Trigger.MISFIRE_INSTRUCTION_SMART_POLICY) { + if (getRepeatCount() == 0) { + instr = MISFIRE_INSTRUCTION_FIRE_NOW; + } else if (getRepeatCount() == REPEAT_INDEFINITELY) { + instr = MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT; + } else { + // if (getRepeatCount() > 0) + instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT; + } + } else if (instr == MISFIRE_INSTRUCTION_FIRE_NOW && getRepeatCount() != 0) { + instr = MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT; + } + + if (instr == MISFIRE_INSTRUCTION_FIRE_NOW) { + setNextFireTime(new Date()); + } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT) { + Date newFireTime = getFireTimeAfter(new Date()); + while (newFireTime != null && cal != null + && !cal.isTimeIncluded(newFireTime.getTime())) { + newFireTime = getFireTimeAfter(newFireTime); + + if(newFireTime == null) + break; + + //avoid infinite loop + java.util.Calendar c = java.util.Calendar.getInstance(); + c.setTime(newFireTime); + if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) { + newFireTime = null; + } + } + setNextFireTime(newFireTime); + } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT) { + Date newFireTime = getFireTimeAfter(new Date()); + while (newFireTime != null && cal != null + && !cal.isTimeIncluded(newFireTime.getTime())) { + newFireTime = getFireTimeAfter(newFireTime); + + if(newFireTime == null) + break; + + //avoid infinite loop + java.util.Calendar c = java.util.Calendar.getInstance(); + c.setTime(newFireTime); + if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) { + newFireTime = null; + } + } + if (newFireTime != null) { + int timesMissed = computeNumTimesFiredBetween(nextFireTime, + newFireTime); + setTimesTriggered(getTimesTriggered() + timesMissed); + } + + setNextFireTime(newFireTime); + } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT) { + Date newFireTime = new Date(); + if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) { + setRepeatCount(getRepeatCount() - getTimesTriggered()); + setTimesTriggered(0); + } + + if (getEndTime() != null && getEndTime().before(newFireTime)) { + setNextFireTime(null); // We are past the end time + } else { + setStartTime(newFireTime); + setNextFireTime(newFireTime); + } + } else if (instr == MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT) { + Date newFireTime = new Date(); + + int timesMissed = computeNumTimesFiredBetween(nextFireTime, + newFireTime); + + if (repeatCount != 0 && repeatCount != REPEAT_INDEFINITELY) { + int remainingCount = getRepeatCount() + - (getTimesTriggered() + timesMissed); + if (remainingCount <= 0) { + remainingCount = 0; + } + setRepeatCount(remainingCount); + setTimesTriggered(0); + } + + if (getEndTime() != null && getEndTime().before(newFireTime)) { + setNextFireTime(null); // We are past the end time + } else { + setStartTime(newFireTime); + setNextFireTime(newFireTime); + } + } + + } + + /** + *

+ * 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). + *

+ * + * @see #executionComplete(JobExecutionContext, JobExecutionException) + */ + @Override + public void triggered(Calendar calendar) { + timesTriggered++; + previousFireTime = nextFireTime; + nextFireTime = getFireTimeAfter(nextFireTime); + + 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) { + nextFireTime = null; + } + } + } + + /** + * @see org.quartz.impl.triggers.AbstractTrigger#updateWithNewCalendar(org.quartz.Calendar, long) + */ + @Override + public void updateWithNewCalendar(Calendar calendar, long misfireThreshold) + { + nextFireTime = getFireTimeAfter(previousFireTime); + + if (nextFireTime == null || calendar == null) { + return; + } + + Date now = new Date(); + while (nextFireTime != 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) { + nextFireTime = null; + } + + if(nextFireTime != null && nextFireTime.before(now)) { + long diff = now.getTime() - nextFireTime.getTime(); + if(diff >= misfireThreshold) { + nextFireTime = getFireTimeAfter(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. + *

+ * + * @return the first time at which the Trigger will be fired + * by the scheduler, which is also the same value getNextFireTime() + * will return (until after the first firing of the Trigger). + *

+ */ + @Override + 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; + } + + /** + *

+ * 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. + *

+ * + * @see TriggerUtils#computeFireTimesBetween(org.quartz.spi.OperableTrigger, org.quartz.Calendar, java.util.Date, java.util.Date) + */ + @Override + public Date getNextFireTime() { + return nextFireTime; + } + + /** + *

+ * Returns the previous time at which the SimpleTrigger + * fired. If the trigger has not yet fired, null will be + * returned. + */ + @Override + 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. + *

+ */ + @Override + public Date getFireTimeAfter(Date afterTime) { + if (complete) { + return null; + } + + if ((timesTriggered > repeatCount) + && (repeatCount != REPEAT_INDEFINITELY)) { + return null; + } + + if (afterTime == null) { + afterTime = new Date(); + } + + if (repeatCount == 0 && afterTime.compareTo(getStartTime()) >= 0) { + return null; + } + + long startMillis = getStartTime().getTime(); + long afterMillis = afterTime.getTime(); + long endMillis = (getEndTime() == null) ? Long.MAX_VALUE : getEndTime() + .getTime(); + + if (endMillis <= afterMillis) { + return null; + } + + if (afterMillis < startMillis) { + return new Date(startMillis); + } + + long numberOfTimesExecuted = ((afterMillis - startMillis) / repeatInterval) + 1; + + if ((numberOfTimesExecuted > repeatCount) && + (repeatCount != REPEAT_INDEFINITELY)) { + return null; + } + + Date time = new Date(startMillis + (numberOfTimesExecuted * repeatInterval)); + + if (endMillis <= time.getTime()) { + return null; + } + + return time; + } + + /** + *

+ * 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. + *

+ */ + public Date getFireTimeBefore(Date end) { + if (end.getTime() < getStartTime().getTime()) { + return null; + } + + int numFires = computeNumTimesFiredBetween(getStartTime(), end); + + return new Date(getStartTime().getTime() + (numFires * repeatInterval)); + } + + public int computeNumTimesFiredBetween(Date start, Date end) { + + if(repeatInterval < 1) { + return 0; + } + + long time = end.getTime() - start.getTime(); + + return (int) (time / repeatInterval); + } + + /** + *

+ * 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. + *

+ */ + @Override + 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. + *

+ */ + @Override + public boolean mayFireAgain() { + return (getNextFireTime() != null); + } + + /** + *

+ * 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. + */ + @Override + public void validate() throws SchedulerException { + super.validate(); + + if (repeatCount != 0 && repeatInterval < 1) { + throw new SchedulerException("Repeat Interval cannot be zero."); + } + } + + /** + * 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; + } + + /** + * Get a {@link ScheduleBuilder} that is configured to produce a + * schedule identical to this trigger's schedule. + * + * @see #getTriggerBuilder() + */ + @Override + public ScheduleBuilder getScheduleBuilder() { + + SimpleScheduleBuilder sb = SimpleScheduleBuilder.simpleSchedule() + .withIntervalInMilliseconds(getRepeatInterval()) + .withRepeatCount(getRepeatCount()); + + switch(getMisfireInstruction()) { + case MISFIRE_INSTRUCTION_FIRE_NOW : sb.withMisfireHandlingInstructionFireNow(); + break; + case MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT : sb.withMisfireHandlingInstructionNextWithExistingCount(); + break; + case MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT : sb.withMisfireHandlingInstructionNextWithRemainingCount(); + break; + case MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT : sb.withMisfireHandlingInstructionNowWithExistingCount(); + break; + case MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT : sb.withMisfireHandlingInstructionNowWithRemainingCount(); + break; + } + + return sb; + } + +} Index: 3rdParty_sources/quartz/org/quartz/impl/triggers/package.html =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/impl/triggers/package.html (revision 0) +++ 3rdParty_sources/quartz/org/quartz/impl/triggers/package.html (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,15 @@ + + +Package org.quartz.triggers + + +

This package contains Trigger implementations that ship with Quartz. End-users should not directly use these classes, +but rather use the builders and interfaces found in the main org.quartz package.

+ +
+
+
+See the Quartz project for more information. + + + \ No newline at end of file Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/jobs/FileScanJob.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/jobs/FileScanListener.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/jobs/NativeJob.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/jobs/NoOpJob.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/jobs/ee/ejb/EJBInvokerJob.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/jobs/ee/jmx/JMXInvokerJob.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/jobs/ee/mail/SendMailJob.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/listeners/BroadcastJobListener.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/listeners/BroadcastJobListener.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/listeners/BroadcastJobListener.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,128 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.listeners; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobListener; + +/** + * Holds a List of references to JobListener instances and broadcasts all + * events to them (in order). + * + *

The broadcasting behavior of this listener to delegate listeners 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(org.quartz.JobListener) + * @see #removeListener(org.quartz.JobListener) + * @see #removeListener(String) + * + * @author James House (jhouse AT revolition DOT net) + */ +public class BroadcastJobListener implements JobListener { + + private String name; + private List listeners; + + /** + * Construct an instance with the given name. + * + * (Remember to add some delegate listeners!) + * + * @param name the name of this instance + */ + public BroadcastJobListener(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 BroadcastJobListener(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 = itr.next(); + if(jl.getName().equals(listenerName)) { + itr.remove(); + return true; + } + } + return false; + } + + public List getListeners() { + return java.util.Collections.unmodifiableList(listeners); + } + + public void jobToBeExecuted(JobExecutionContext context) { + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + JobListener jl = itr.next(); + jl.jobToBeExecuted(context); + } + } + + public void jobExecutionVetoed(JobExecutionContext context) { + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + JobListener jl = itr.next(); + jl.jobExecutionVetoed(context); + } + } + + public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + JobListener jl = itr.next(); + jl.jobWasExecuted(context, jobException); + } + } + +} Index: 3rdParty_sources/quartz/org/quartz/listeners/BroadcastSchedulerListener.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/listeners/BroadcastSchedulerListener.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/listeners/BroadcastSchedulerListener.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,219 @@ +package org.quartz.listeners; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.SchedulerException; +import org.quartz.SchedulerListener; +import org.quartz.Trigger; +import org.quartz.TriggerKey; + +/** + * Holds a List of references to SchedulerListener instances and broadcasts all + * events to them (in order). + * + *

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(org.quartz.SchedulerListener) + * @see #removeListener(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 jobAdded(JobDetail jobDetail) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.jobAdded(jobDetail); + } + } + + public void jobDeleted(JobKey jobKey) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.jobDeleted(jobKey); + } + } + + public void jobScheduled(Trigger trigger) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.jobScheduled(trigger); + } + } + + public void jobUnscheduled(TriggerKey triggerKey) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.jobUnscheduled(triggerKey); + } + } + + public void triggerFinalized(Trigger trigger) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.triggerFinalized(trigger); + } + } + + public void triggerPaused(TriggerKey key) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.triggerPaused(key); + } + } + + public void triggersPaused(String triggerGroup) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.triggersPaused(triggerGroup); + } + } + + public void triggerResumed(TriggerKey key) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.triggerResumed(key); + } + } + + public void triggersResumed(String triggerGroup) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.triggersResumed(triggerGroup); + } + } + + public void schedulingDataCleared() { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.schedulingDataCleared(); + } + } + + + public void jobPaused(JobKey key) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.jobPaused(key); + } + } + + public void jobsPaused(String jobGroup) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.jobsPaused(jobGroup); + } + } + + public void jobResumed(JobKey key) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.jobResumed(key); + } + } + + public void jobsResumed(String jobGroup) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.jobsResumed(jobGroup); + } + } + + public void schedulerError(String msg, SchedulerException cause) { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.schedulerError(msg, cause); + } + } + + public void schedulerStarted() { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.schedulerStarted(); + } + } + + public void schedulerStarting() { + Iterator itr = listeners.iterator(); + while (itr.hasNext()) { + SchedulerListener l = itr.next(); + l.schedulerStarting(); + } + } + + public void schedulerInStandbyMode() { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.schedulerInStandbyMode(); + } + } + + public void schedulerShutdown() { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.schedulerShutdown(); + } + } + + public void schedulerShuttingdown() { + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + SchedulerListener l = itr.next(); + l.schedulerShuttingdown(); + } + } + +} Index: 3rdParty_sources/quartz/org/quartz/listeners/BroadcastTriggerListener.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/listeners/BroadcastTriggerListener.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/listeners/BroadcastTriggerListener.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,140 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.listeners; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.quartz.JobExecutionContext; +import org.quartz.Trigger; +import org.quartz.TriggerListener; +import org.quartz.Trigger.CompletedExecutionInstruction; + +/** + * Holds a List of references to TriggerListener instances and broadcasts all + * events to them (in order). + * + *

The broadcasting behavior of this listener to delegate listeners 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(org.quartz.TriggerListener) + * @see #removeListener(org.quartz.TriggerListener) + * @see #removeListener(String) + * + * @author James House (jhouse AT revolition DOT net) + */ +public class BroadcastTriggerListener implements TriggerListener { + + private String name; + private List listeners; + + /** + * Construct an instance with the given name. + * + * (Remember to add some delegate listeners!) + * + * @param name the name of this instance + */ + public BroadcastTriggerListener(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 BroadcastTriggerListener(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 = itr.next(); + if(l.getName().equals(listenerName)) { + itr.remove(); + return true; + } + } + return false; + } + + public List getListeners() { + return java.util.Collections.unmodifiableList(listeners); + } + + public void triggerFired(Trigger trigger, JobExecutionContext context) { + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + TriggerListener l = itr.next(); + l.triggerFired(trigger, context); + } + } + + public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + TriggerListener l = itr.next(); + if(l.vetoJobExecution(trigger, context)) { + return true; + } + } + return false; + } + + public void triggerMisfired(Trigger trigger) { + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + TriggerListener l = itr.next(); + l.triggerMisfired(trigger); + } + } + + public void triggerComplete(Trigger trigger, JobExecutionContext context, CompletedExecutionInstruction triggerInstructionCode) { + + Iterator itr = listeners.iterator(); + while(itr.hasNext()) { + TriggerListener l = itr.next(); + l.triggerComplete(trigger, context, triggerInstructionCode); + } + } + +} Index: 3rdParty_sources/quartz/org/quartz/listeners/JobChainingJobListener.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/listeners/JobChainingJobListener.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/listeners/JobChainingJobListener.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,105 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.listeners; + +import java.util.HashMap; +import java.util.Map; + +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobKey; +import 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 JobKey with the name and group of the first job + * @param secondJob a JobKey with the name and group of the follow-up job + */ + public void addJobChainLink(JobKey firstJob, JobKey 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); + } + + @Override + public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { + + JobKey sj = chainLinks.get(context.getJobDetail().getKey()); + + if(sj == null) { + return; + } + + getLog().info("Job '" + context.getJobDetail().getKey() + "' will now chain to Job '" + sj + "'"); + + try { + context.getScheduler().triggerJob(sj); + } catch(SchedulerException se) { + getLog().error("Error encountered during chaining to Job '" + sj + "'", se); + } + } +} + Index: 3rdParty_sources/quartz/org/quartz/listeners/JobListenerSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/listeners/JobListenerSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/listeners/JobListenerSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,60 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.listeners; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.quartz.JobListener; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * A helpful abstract base class for implementors of + * {@link org.quartz.JobListener}. + * + *

+ * The methods in this class are empty so you only need to override the + * subset for the {@link org.quartz.JobListener} events + * you care about. + *

+ * + *

+ * You are required to implement {@link org.quartz.JobListener#getName()} + * to return the unique name of your JobListener. + *

+ * + * @see org.quartz.JobListener + */ +public abstract class JobListenerSupport implements JobListener { + private final Logger log = LoggerFactory.getLogger(getClass()); + + /** + * Get the {@link org.slf4j.Logger} for this + * class's category. This should be used by subclasses for logging. + */ + protected Logger getLog() { + return log; + } + + public void jobToBeExecuted(JobExecutionContext context) { + } + + public void jobExecutionVetoed(JobExecutionContext context) { + } + + public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { + } +} Index: 3rdParty_sources/quartz/org/quartz/listeners/SchedulerListenerSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/listeners/SchedulerListenerSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/listeners/SchedulerListenerSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,110 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.listeners; + +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.SchedulerException; +import org.quartz.SchedulerListener; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A helpful abstract base class for implementors of + * {@link org.quartz.SchedulerListener}. + * + *

+ * The methods in this class are empty so you only need to override the + * subset for the {@link org.quartz.SchedulerListener} events + * you care about. + *

+ * + * @see org.quartz.SchedulerListener + */ +public abstract class SchedulerListenerSupport implements SchedulerListener { + private final Logger log = LoggerFactory.getLogger(getClass()); + + /** + * Get the {@link org.slf4j.Logger} for this + * class's category. This should be used by subclasses for logging. + */ + protected Logger getLog() { + return log; + } + + public void jobAdded(JobDetail jobDetail) { + } + + public void jobDeleted(JobKey jobKey) { + } + + public void jobPaused(JobKey jobKey) { + } + + public void jobResumed(JobKey jobKey) { + } + + public void jobScheduled(Trigger trigger) { + } + + public void jobsPaused(String jobGroup) { + } + + public void jobsResumed(String jobGroup) { + } + + public void jobUnscheduled(TriggerKey triggerKey) { + } + + public void schedulerError(String msg, SchedulerException cause) { + } + + public void schedulerInStandbyMode() { + } + + public void schedulerShutdown() { + } + + public void schedulerShuttingdown() { + } + + public void schedulerStarted() { + } + + public void schedulerStarting() { + } + + public void triggerFinalized(Trigger trigger) { + } + + public void triggerPaused(TriggerKey triggerKey) { + } + + public void triggerResumed(TriggerKey triggerKey) { + } + + public void triggersPaused(String triggerGroup) { + } + + public void triggersResumed(String triggerGroup) { + } + + public void schedulingDataCleared() { + } + +} Index: 3rdParty_sources/quartz/org/quartz/listeners/TriggerListenerSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/listeners/TriggerListenerSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/listeners/TriggerListenerSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,68 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.listeners; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.quartz.TriggerListener; +import org.quartz.Trigger; +import org.quartz.JobExecutionContext; +import org.quartz.Trigger.CompletedExecutionInstruction; + +/** + * A helpful abstract base class for implementors of + * {@link org.quartz.TriggerListener}. + * + *

+ * The methods in this class are empty so you only need to override the + * subset for the {@link org.quartz.TriggerListener} events + * you care about. + *

+ * + *

+ * You are required to implement {@link org.quartz.TriggerListener#getName()} + * to return the unique name of your TriggerListener. + *

+ * + * @see org.quartz.TriggerListener + */ +public abstract class TriggerListenerSupport implements TriggerListener { + private final Logger log = LoggerFactory.getLogger(getClass()); + + /** + * Get the {@link org.slf4j.Logger} for this + * class's category. This should be used by subclasses for logging. + */ + protected Logger 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, + CompletedExecutionInstruction triggerInstructionCode) { + } +} Index: 3rdParty_sources/quartz/org/quartz/management/ManagementRESTServiceConfiguration.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/management/ManagementRESTServiceConfiguration.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/management/ManagementRESTServiceConfiguration.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,248 @@ +/** + * Copyright Terracotta, Inc. + * + * 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 org.quartz.management; + + +/** + * Configuration class of management REST services. + * + * @author Ludovic Orban + * + * TODO : could be merged with ehcache + * ManagementRESTServiceConfiguration in a common module + */ +public class ManagementRESTServiceConfiguration { + + /** + * Default bind value. + */ + public static final String DEFAULT_BIND = "0.0.0.0:9888"; + + /** + * Default timeout for the connection to the configured security service + */ + public static final int DEFAULT_SECURITY_SVC_TIMEOUT = 5 * 1000; + + private volatile boolean enabled = false; + private volatile String securityServiceLocation; + private volatile int securityServiceTimeout = DEFAULT_SECURITY_SVC_TIMEOUT; + private volatile String bind = DEFAULT_BIND; + + // private volatile int sampleHistorySize = + // CacheStatisticsSampler.DEFAULT_HISTORY_SIZE; + // private volatile int sampleIntervalSeconds = + // CacheStatisticsSampler.DEFAULT_INTERVAL_SECS; + // private volatile int sampleSearchIntervalSeconds = + // CacheStatisticsSampler.DEFAULT_SEARCH_INTERVAL_SEC; + + /** + * Check if the REST services should be enabled or not. + * @return true if REST services should be enabled. + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Set that the REST services should be enabled or disabled. + * @param enabled true if the REST services should be enabled. + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * Returns the security service location required for trusted identity assertion to the embedded REST management + * service. This feature is only available with an enterprise license. + *

+ * If this value is set, then this service will require secure dialog with the TMS or other 3rd party REST client + * implementations. The service furnished by the enterprise version of the TMC is located is provided at /api/assertIdentity. + * + * + * @return a string representing the URL of the security service. + */ + public String getSecurityServiceLocation() { + return securityServiceLocation; + } + + /** + * Sets the security service location required for trusted identity assertion to the embedded REST management + * service. This feature is only available with an enterprise license. + *

+ * If this value is set, then this service will require secure dialog with the TMS or other 3rd party REST client + * implementations. The service furnished by the enterprise version of the TMC is located is provided at /api/assertIdentity. + * + * @param securityServiceURL a string representing the URL of the security service. + */ + public void setSecurityServiceLocation(String securityServiceURL) { + this.securityServiceLocation = securityServiceURL; + } + + /** + * Returns the connection/read timeout value for the security service in milliseconds. + * + * @return security service timeout + */ + public int getSecurityServiceTimeout() { + return securityServiceTimeout; + } + + /** + * Sets the connection/read timeout value for the security service in milliseconds. + * + * @param securityServiceTimeout milliseconds to timeout + */ + public void setSecurityServiceTimeout(int securityServiceTimeout) { + this.securityServiceTimeout = securityServiceTimeout; + } + + /** + * Get the host:port pair to which the REST server should be bound. + * Format is: [IP address|host name]:[port number] + * @return the host:port pair to which the REST server should be bound. + */ + public String getBind() { + return bind; + } + + /** + * Get the host part of the host:port pair to which the REST server should be bound. + * @return the host part of the host:port pair to which the REST server should be bound. + */ + public String getHost() { + if (bind == null) { + return null; + } + return bind.split("\\:")[0]; + } + + /** + * Get the port part of the host:port pair to which the REST server should be bound. + * @return the port part of the host:port pair to which the REST server should be bound. + */ + public int getPort() { + if (bind == null) { + return -1; + } + String[] split = bind.split("\\:"); + if (split.length != 2) { + throw new IllegalArgumentException("invalid bind format (should be IP:port)"); + } + return Integer.parseInt(split[1]); + } + + /** + * Set the host:port pair to which the REST server should be bound. + * @param bind host:port pair to which the REST server should be bound. + */ + public void setBind(String bind) { + this.bind = bind; + } + + /** + * Returns the sample history size to be applied to the {@link SampledCounterConfig} for sampled statistics + * + * @return the sample history size + */ + // public int getSampleHistorySize() { + // return sampleHistorySize; + // } + + /** + * Sets the sample history size to be applied to the {@link SampledCounterConfig} for sampled statistics + * + * @param sampleHistorySize to set + */ + // public void setSampleHistorySize(final int sampleHistorySize) { + // this.sampleHistorySize = sampleHistorySize; + // } + + /** + * Returns the sample interval in seconds to be applied to the {@link SampledCounterConfig} for sampled statistics + * + * @return the sample interval in seconds + */ + // public int getSampleIntervalSeconds() { + // return sampleIntervalSeconds; + // } + + /** + * Sets the sample interval in seconds to be applied to the {@link SampledCounterConfig} for sampled statistics + * + * @param sampleIntervalSeconds to set + */ + // public void setSampleIntervalSeconds(final int sampleIntervalSeconds) { + // this.sampleIntervalSeconds = sampleIntervalSeconds; + // } + + /** + * Returns the sample search interval in seconds to be applied to the {@link SampledRateCounterConfig} for sampled statistics + * + * @return the sample search interval in seconds + */ + // public int getSampleSearchIntervalSeconds() { + // return sampleSearchIntervalSeconds; + // } + + /** + * Sets the sample search interval in seconds to be applied to the {@link SampledCounterConfig} for sampled statistics + * + * @param sampleSearchInterval to set + */ + // public void setSampleSearchIntervalSeconds(final int + // sampleSearchInterval) { + // this.sampleSearchIntervalSeconds = sampleSearchInterval; + // } + + /** + * A factory method for {@link SampledCounterConfig} based on the global settings defined on this object + * + * @see #getSampleIntervalSeconds() + * @see #getSampleHistorySize() + * + * @return a {@code SampledCounterConfig} + */ + // public SampledCounterConfig makeSampledCounterConfig() { + // return new SampledCounterConfig(getSampleIntervalSeconds(), + // getSampleHistorySize(), true, 0L); + // } + + /** + * A factory method for {@link SampledCounterConfig} based on the global settings defined on this object + * + * @see #getSampleIntervalSeconds() + * @see #getSampleHistorySize() + * + * @return a {@code SampledCounterConfig} + */ + // public SampledRateCounterConfig makeSampledGetRateCounterConfig() { + // return new SampledRateCounterConfig(getSampleIntervalSeconds(), + // getSampleHistorySize(), true); + // } + + /** + * A factory method for {@link SampledCounterConfig} based on the global settings defined on this object + * + * @see #getSampleSearchIntervalSeconds() + * @see #getSampleHistorySize() + * + * @return a {@code SampledCounterConfig} + */ + // public SampledRateCounterConfig makeSampledSearchRateCounterConfig() { + // return new SampledRateCounterConfig(getSampleSearchIntervalSeconds(), + // getSampleHistorySize(), true); + // } +} Index: 3rdParty_sources/quartz/org/quartz/management/ManagementServer.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/management/ManagementServer.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/management/ManagementServer.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,62 @@ +package org.quartz.management; + +import org.quartz.core.QuartzScheduler; + + +/** + * Copyright Terracotta, Inc. + * + * 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. + */ + + +/** + * Interface implemented by management servers. + * + * @author Ludovic Orban + * @author brandony + */ +public interface ManagementServer { + + /** + * Start the management server + */ + public void start(); + + /** + * Stop the management server + */ + public void stop(); + + /** + * Puts the submitted resource under the purview of this {@code ManagementServer}. + * + * @param managedResource the resource to be managed + */ + public void register(QuartzScheduler managedResource); + + /** + * Removes the submitted resource under the purview of this {@code ManagementServer}. + * + * @param managedResource the resource to be managed + */ + public void unregister(QuartzScheduler managedResource); + + /** + * Returns true if this {@code ManagementServer} has any resources registered. + * + * @return true if actively managing resources, false if not. + */ + public boolean hasRegistered(); + +} Index: 3rdParty_sources/quartz/org/quartz/package.html =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/package.html (.../package.html) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/package.html (.../package.html) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -8,8 +8,32 @@


-See the Quartz project - at Open Symphony for more information. +

See the Quartz project for more information.

+ +

Quartz provides a builder-style API for constructing scheduling-related +entities via a Domain-Specific Language (DSL). The DSL can best be +utilized through the usage of static imports of the methods on the classes +TriggerBuilder, JobBuilder, +DateBuilder, JobKey, TriggerKey +and the various ScheduleBuilder implementations.

+ +

Client code can then use the DSL to write code such as this:

+
+        JobDetail job = newJob(MyJob.class)
+            .withIdentity("myJob")
+            .build();
+            
+        Trigger trigger = newTrigger() 
+            .withIdentity(triggerKey("myTrigger", "myTriggerGroup"))
+            .withSchedule(simpleSchedule()
+                .withIntervalInHours(1)
+                .repeatForever())
+            .startAt(futureDate(10, MINUTES))
+            .build();
+        
+        scheduler.scheduleJob(job, trigger);
+
+ \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/plugins/SchedulerPluginWithUserTransactionSupport.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/plugins/SchedulerPluginWithUserTransactionSupport.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/plugins/SchedulerPluginWithUserTransactionSupport.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,205 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.plugins; + +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.ee.jta.UserTransactionHelper; +import org.quartz.spi.SchedulerPlugin; + +/** + * Base class for plugins that wish to support having their start and + * shutdown methods run within a 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 Logger log = LoggerFactory.getLogger(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 Logger for this class. + */ + protected Logger getLog() { + return log; + } + + /** + * Get the name of this plugin. Set as part of initialize(). + */ + protected String getName() { + return name; + } + + /** + * Get this plugin's Scheduler. Set as part of initialize(). + */ + protected Scheduler getScheduler() { + return scheduler; + } + + public void initialize(String pname, Scheduler sched) throws SchedulerException { + this.name = pname; + this.scheduler = sched; + } + + /** + * 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); + } + } + } +} Index: 3rdParty_sources/quartz/org/quartz/plugins/history/LoggingJobHistoryPlugin.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/plugins/history/LoggingJobHistoryPlugin.java (.../LoggingJobHistoryPlugin.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/plugins/history/LoggingJobHistoryPlugin.java (.../LoggingJobHistoryPlugin.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,19 +15,18 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.plugins.history; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.JobListener; +import org.quartz.impl.matchers.EverythingMatcher; +import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.SchedulerPlugin; import java.text.MessageFormat; @@ -291,7 +290,9 @@ private String jobFailedMessage = "Job {1}.{0} execution failed at {2, date, HH:mm:ss MM/dd/yyyy} and reports: {8}"; private String jobWasVetoedMessage = "Job {1}.{0} was vetoed. It was to be fired (by trigger {4}.{3}) at: {2, date, HH:mm:ss MM/dd/yyyy}"; - + + private final Logger log = LoggerFactory.getLogger(getClass()); + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -311,15 +312,13 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - protected Log getLog() { - return LogFactory.getLog(LoggingJobHistoryPlugin.class); + protected Logger getLog() { + return log; } /** * Get the message that is logged when a Job successfully completes its * execution. - * - * @return String */ public String getJobSuccessMessage() { return jobSuccessMessage; @@ -328,17 +327,13 @@ /** * Get the message that is logged when a Job fails its * execution. - * - * @return String */ public String getJobFailedMessage() { return jobFailedMessage; } /** * Get the message that is logged when a Job is about to execute. - * - * @return String */ public String getJobToBeFiredMessage() { return jobToBeFiredMessage; @@ -348,7 +343,7 @@ * Set the message that is logged when a Job successfully completes its * execution. * - * @param jobCompleteMessage + * @param jobSuccessMessage * String in java.text.MessageFormat syntax. */ public void setJobSuccessMessage(String jobSuccessMessage) { @@ -359,7 +354,7 @@ * Set the message that is logged when a Job fails its * execution. * - * @param jobCompleteMessage + * @param jobFailedMessage * String in java.text.MessageFormat syntax. */ public void setJobFailedMessage(String jobFailedMessage) { @@ -379,8 +374,6 @@ /** * Get the message that is logged when a Job execution is vetoed by a * trigger listener. - * - * @return String */ public String getJobWasVetoedMessage() { return jobWasVetoedMessage; @@ -390,7 +383,7 @@ * Set the message that is logged when a Job execution is vetoed by a * trigger listener. * - * @param jobToBeFiredMessage + * @param jobWasVetoedMessage * String in java.text.MessageFormat syntax. */ public void setJobWasVetoedMessage(String jobWasVetoedMessage) { @@ -414,10 +407,10 @@ * @throws SchedulerConfigException * if there is an error initializing. */ - public void initialize(String name, Scheduler scheduler) - throws SchedulerException { - this.name = name; - scheduler.addGlobalJobListener(this); + public void initialize(String pname, Scheduler scheduler,ClassLoadHelper classLoadHelper) + throws SchedulerException { + this.name = pname; + scheduler.getListenerManager().addJobListener(this, EverythingMatcher.allJobs()); } public void start() { @@ -465,11 +458,13 @@ Trigger trigger = context.getTrigger(); - Object[] args = {context.getJobDetail().getName(), - context.getJobDetail().getGroup(), new java.util.Date(), - trigger.getName(), trigger.getGroup(), - trigger.getPreviousFireTime(), trigger.getNextFireTime(), - new Integer(context.getRefireCount())}; + Object[] args = { + context.getJobDetail().getKey().getName(), + context.getJobDetail().getKey().getGroup(), new java.util.Date(), + trigger.getKey().getName(), trigger.getKey().getGroup(), + trigger.getPreviousFireTime(), trigger.getNextFireTime(), + Integer.valueOf(context.getRefireCount()) + }; getLog().info(MessageFormat.format(getJobToBeFiredMessage(), args)); } @@ -490,25 +485,30 @@ } String errMsg = jobException.getMessage(); - args = new Object[]{context.getJobDetail().getName(), - context.getJobDetail().getGroup(), new java.util.Date(), - trigger.getName(), trigger.getGroup(), + args = + new Object[] { + context.getJobDetail().getKey().getName(), + context.getJobDetail().getKey().getGroup(), new java.util.Date(), + trigger.getKey().getName(), trigger.getKey().getGroup(), trigger.getPreviousFireTime(), trigger.getNextFireTime(), - new Integer(context.getRefireCount()), errMsg}; + Integer.valueOf(context.getRefireCount()), errMsg + }; getLog().warn(MessageFormat.format(getJobFailedMessage(), args), jobException); - } - else { + } else { if (!getLog().isInfoEnabled()) { return; } String result = String.valueOf(context.getResult()); - args = new Object[]{context.getJobDetail().getName(), - context.getJobDetail().getGroup(), new java.util.Date(), - trigger.getName(), trigger.getGroup(), + args = + new Object[] { + context.getJobDetail().getKey().getName(), + context.getJobDetail().getKey().getGroup(), new java.util.Date(), + trigger.getKey().getName(), trigger.getKey().getGroup(), trigger.getPreviousFireTime(), trigger.getNextFireTime(), - new Integer(context.getRefireCount()), result}; + Integer.valueOf(context.getRefireCount()), result + }; getLog().info(MessageFormat.format(getJobSuccessMessage(), args)); } @@ -525,11 +525,13 @@ Trigger trigger = context.getTrigger(); - Object[] args = {context.getJobDetail().getName(), - context.getJobDetail().getGroup(), new java.util.Date(), - trigger.getName(), trigger.getGroup(), - trigger.getPreviousFireTime(), trigger.getNextFireTime(), - new Integer(context.getRefireCount())}; + Object[] args = { + context.getJobDetail().getKey().getName(), + context.getJobDetail().getKey().getGroup(), new java.util.Date(), + trigger.getKey().getName(), trigger.getKey().getGroup(), + trigger.getPreviousFireTime(), trigger.getNextFireTime(), + Integer.valueOf(context.getRefireCount()) + }; getLog().info(MessageFormat.format(getJobWasVetoedMessage(), args)); } Index: 3rdParty_sources/quartz/org/quartz/plugins/history/LoggingTriggerHistoryPlugin.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/plugins/history/LoggingTriggerHistoryPlugin.java (.../LoggingTriggerHistoryPlugin.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/plugins/history/LoggingTriggerHistoryPlugin.java (.../LoggingTriggerHistoryPlugin.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,20 +15,20 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.plugins.history; import java.text.MessageFormat; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.quartz.JobExecutionContext; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerListener; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.impl.matchers.EverythingMatcher; +import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.SchedulerPlugin; /** @@ -228,6 +228,8 @@ private String triggerCompleteMessage = "Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {9}"; + private final Logger log = LoggerFactory.getLogger(getClass()); + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -247,8 +249,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - protected Log getLog() { - return LogFactory.getLog(LoggingTriggerHistoryPlugin.class); + protected Logger getLog() { + return log; } /** @@ -327,11 +329,11 @@ * @throws SchedulerConfigException * if there is an error initializing. */ - public void initialize(String name, Scheduler scheduler) - throws SchedulerException { - this.name = name; + public void initialize(String pname, Scheduler scheduler, ClassLoadHelper classLoadHelper) + throws SchedulerException { + this.name = pname; - scheduler.addGlobalTriggerListener(this); + scheduler.getListenerManager().addTriggerListener(this, EverythingMatcher.allTriggers()); } public void start() { @@ -374,11 +376,13 @@ return; } - Object[] args = {trigger.getName(), trigger.getGroup(), - trigger.getPreviousFireTime(), trigger.getNextFireTime(), - new java.util.Date(), context.getJobDetail().getName(), - context.getJobDetail().getGroup(), - new Integer(context.getRefireCount())}; + Object[] args = { + trigger.getKey().getName(), trigger.getKey().getGroup(), + trigger.getPreviousFireTime(), trigger.getNextFireTime(), + new java.util.Date(), context.getJobDetail().getKey().getName(), + context.getJobDetail().getKey().getGroup(), + Integer.valueOf(context.getRefireCount()) + }; getLog().info(MessageFormat.format(getTriggerFiredMessage(), args)); } @@ -388,34 +392,43 @@ return; } - Object[] args = {trigger.getName(), trigger.getGroup(), - trigger.getPreviousFireTime(), trigger.getNextFireTime(), - new java.util.Date(), trigger.getJobGroup(), - trigger.getJobGroup(),}; + Object[] args = { + trigger.getKey().getName(), trigger.getKey().getGroup(), + trigger.getPreviousFireTime(), trigger.getNextFireTime(), + new java.util.Date(), trigger.getJobKey().getName(), + trigger.getJobKey().getGroup() + }; getLog().info(MessageFormat.format(getTriggerMisfiredMessage(), args)); } public void triggerComplete(Trigger trigger, JobExecutionContext context, - int triggerInstructionCode) { + CompletedExecutionInstruction triggerInstructionCode) { if (!getLog().isInfoEnabled()) { return; } String instrCode = "UNKNOWN"; - if (triggerInstructionCode == Trigger.INSTRUCTION_DELETE_TRIGGER) instrCode = "DELETE TRIGGER"; - else if (triggerInstructionCode == Trigger.INSTRUCTION_NOOP) instrCode = "DO NOTHING"; - else if (triggerInstructionCode == Trigger.INSTRUCTION_RE_EXECUTE_JOB) instrCode = "RE-EXECUTE JOB"; - else if (triggerInstructionCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE) instrCode = "SET ALL OF JOB'S TRIGGERS COMPLETE"; - else if (triggerInstructionCode == Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE) - instrCode = "SET THIS TRIGGER COMPLETE"; + if (triggerInstructionCode == CompletedExecutionInstruction.DELETE_TRIGGER) { + instrCode = "DELETE TRIGGER"; + } else if (triggerInstructionCode == CompletedExecutionInstruction.NOOP) { + instrCode = "DO NOTHING"; + } else if (triggerInstructionCode == CompletedExecutionInstruction.RE_EXECUTE_JOB) { + instrCode = "RE-EXECUTE JOB"; + } else if (triggerInstructionCode == CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_COMPLETE) { + instrCode = "SET ALL OF JOB'S TRIGGERS COMPLETE"; + } else if (triggerInstructionCode == CompletedExecutionInstruction.SET_TRIGGER_COMPLETE) { + instrCode = "SET THIS TRIGGER COMPLETE"; + } - Object[] args = {trigger.getName(), trigger.getGroup(), - trigger.getPreviousFireTime(), trigger.getNextFireTime(), - new java.util.Date(), context.getJobDetail().getName(), - context.getJobDetail().getGroup(), - new Integer(context.getRefireCount()), - new Integer(triggerInstructionCode), instrCode}; + Object[] args = { + trigger.getKey().getName(), trigger.getKey().getGroup(), + trigger.getPreviousFireTime(), trigger.getNextFireTime(), + new java.util.Date(), context.getJobDetail().getKey().getName(), + context.getJobDetail().getKey().getGroup(), + Integer.valueOf(context.getRefireCount()), + triggerInstructionCode.toString(), instrCode + }; getLog().info(MessageFormat.format(getTriggerCompleteMessage(), args)); } Index: 3rdParty_sources/quartz/org/quartz/plugins/management/ShutdownHookPlugin.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/plugins/management/ShutdownHookPlugin.java (.../ShutdownHookPlugin.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/plugins/management/ShutdownHookPlugin.java (.../ShutdownHookPlugin.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,15 +15,13 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.plugins.management; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.quartz.Scheduler; import org.quartz.SchedulerException; +import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.SchedulerPlugin; /** @@ -44,12 +42,10 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private String name; - - private Scheduler scheduler; - private boolean cleanShutdown = true; + private final Logger log = LoggerFactory.getLogger(getClass()); + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -97,8 +93,8 @@ cleanShutdown = b; } - protected static Log getLog() { - return LogFactory.getLog(ShutdownHookPlugin.class); + protected Logger getLog() { + return log; } /* @@ -118,15 +114,14 @@ * @throws SchedulerConfigException * if there is an error initializing. */ - public void initialize(String name, final Scheduler scheduler) - throws SchedulerException { - this.name = name; - this.scheduler = scheduler; + public void initialize(String name, final Scheduler scheduler, ClassLoadHelper classLoadHelper) + throws SchedulerException { getLog().info("Registering Quartz shutdown hook."); Thread t = new Thread("Quartz Shutdown-Hook " + scheduler.getSchedulerName()) { + @Override public void run() { getLog().info("Shutting down Quartz..."); try { Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/plugins/xml/JobInitializationPlugin.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/plugins/xml/JobInitializationPluginMultiple.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/plugins/xml/XMLSchedulingDataProcessorPlugin.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/plugins/xml/XMLSchedulingDataProcessorPlugin.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/plugins/xml/XMLSchedulingDataProcessorPlugin.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,424 @@ +/* + * Copyright 2001-2010 Terracotta, Inc. + * + * 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 org.quartz.plugins.xml; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import javax.transaction.UserTransaction; + +import org.quartz.*; +import org.quartz.impl.JobDetailImpl; +import org.quartz.impl.triggers.SimpleTriggerImpl; +import org.quartz.jobs.FileScanJob; +import org.quartz.jobs.FileScanListener; +import org.quartz.plugins.SchedulerPluginWithUserTransactionSupport; +import org.quartz.simpl.CascadingClassLoadHelper; +import org.quartz.spi.ClassLoadHelper; +import org.quartz.xml.XMLSchedulingDataProcessor; + +import static org.quartz.JobBuilder.newJob; +import static org.quartz.SimpleScheduleBuilder.simpleSchedule; +import static org.quartz.TriggerBuilder.newTrigger; + +/** + * This plugin loads XML file(s) to add jobs and schedule them with triggers + * as the scheduler is initialized, and can optionally periodically scan the + * file for changes. + * + *

The XML schema definition can be found here: + * http://www.quartz-scheduler.org/xml/job_scheduling_data_2_0.xsd

+ * + *

+ * The periodically scanning of files for changes is not currently supported in a + * clustered environment. + *

+ * + *

+ * If using this plugin with JobStoreCMT, be sure to set the + * plugin property wrapInUserTransaction to true. Also, if you have a + * positive scanInterval be sure to set + * org.quartz.scheduler.wrapJobExecutionInUserTransaction to true. + *

+ * + * @see org.quartz.xml.XMLSchedulingDataProcessor + * + * @author James House + * @author Pierre Awaragi + * @author pl47ypus + */ +public class XMLSchedulingDataProcessorPlugin + extends SchedulerPluginWithUserTransactionSupport + implements FileScanListener { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + private static final int MAX_JOB_TRIGGER_NAME_LEN = 80; + private static final String JOB_INITIALIZATION_PLUGIN_NAME = "JobSchedulingDataLoaderPlugin"; + private static final String FILE_NAME_DELIMITERS = ","; + + private boolean failOnFileNotFound = true; + + private String fileNames = XMLSchedulingDataProcessor.QUARTZ_XML_DEFAULT_FILE_NAME; + + // Populated by initialization + private Map jobFiles = new LinkedHashMap(); + + private long scanInterval = 0; + + boolean started = false; + + protected ClassLoadHelper classLoadHelper = null; + + private Set jobTriggerNameSet = new HashSet(); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public XMLSchedulingDataProcessorPlugin() { + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Comma separated list of file names (with paths) to the XML files that should be read. + */ + public String getFileNames() { + return fileNames; + } + + /** + * The file name (and path) to the XML file that should be read. + */ + public void setFileNames(String fileNames) { + this.fileNames = fileNames; + } + + /** + * The interval (in seconds) at which to scan for changes to the file. + * If the file has been changed, it is re-loaded and parsed. The default + * value for the interval is 0, which disables scanning. + * + * @return Returns the scanInterval. + */ + public long getScanInterval() { + return scanInterval / 1000; + } + + /** + * The interval (in seconds) at which to scan for changes to the file. + * If the file has been changed, it is re-loaded and parsed. The default + * value for the interval is 0, which disables scanning. + * + * @param scanInterval The scanInterval to set. + */ + public void setScanInterval(long scanInterval) { + this.scanInterval = scanInterval * 1000; + } + + /** + * Whether or not initialization of the plugin should fail (throw an + * exception) if the file cannot be found. Default is true. + */ + public boolean isFailOnFileNotFound() { + return failOnFileNotFound; + } + + /** + * Whether or not initialization of the plugin should fail (throw an + * exception) if the file cannot be found. Default is true. + */ + public void setFailOnFileNotFound(boolean failOnFileNotFound) { + this.failOnFileNotFound = failOnFileNotFound; + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * SchedulerPlugin Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Called during creation of the Scheduler in order to give + * the SchedulerPlugin a chance to initialize. + *

+ * + * @throws org.quartz.SchedulerConfigException + * if there is an error initializing. + */ + public void initialize(String name, final Scheduler scheduler, ClassLoadHelper schedulerFactoryClassLoadHelper) + throws SchedulerException { + super.initialize(name, scheduler); + this.classLoadHelper = schedulerFactoryClassLoadHelper; + + getLog().info("Registering Quartz Job Initialization Plug-in."); + + // Create JobFile objects + StringTokenizer stok = new StringTokenizer(fileNames, FILE_NAME_DELIMITERS); + while (stok.hasMoreTokens()) { + final String fileName = stok.nextToken(); + final JobFile jobFile = new JobFile(fileName); + jobFiles.put(fileName, jobFile); + } + } + + + @Override + public void start(UserTransaction userTransaction) { + try { + if (jobFiles.isEmpty() == false) { + + if (scanInterval > 0) { + getScheduler().getContext().put(JOB_INITIALIZATION_PLUGIN_NAME + '_' + getName(), this); + } + + Iterator iterator = jobFiles.values().iterator(); + while (iterator.hasNext()) { + JobFile jobFile = iterator.next(); + + if (scanInterval > 0) { + String jobTriggerName = buildJobTriggerName(jobFile.getFileBasename()); + TriggerKey tKey = new TriggerKey(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME); + + // remove pre-existing job/trigger, if any + getScheduler().unscheduleJob(tKey); + + JobDetail job = newJob().withIdentity(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME).ofType(FileScanJob.class) + .usingJobData(FileScanJob.FILE_NAME, jobFile.getFileName()) + .usingJobData(FileScanJob.FILE_SCAN_LISTENER_NAME, JOB_INITIALIZATION_PLUGIN_NAME + '_' + getName()) + .build(); + + SimpleTrigger trig = newTrigger().withIdentity(tKey).withSchedule( + simpleSchedule().repeatForever().withIntervalInMilliseconds(scanInterval)) + .forJob(job) + .build(); + + getScheduler().scheduleJob(job, trig); + getLog().debug("Scheduled file scan job for data file: {}, at interval: {}", jobFile.getFileName(), scanInterval); + } + + processFile(jobFile); + } + } + } catch(SchedulerException se) { + getLog().error("Error starting background-task for watching jobs file.", se); + } finally { + started = true; + } + } + + /** + * Helper method for generating unique job/trigger name for the + * file scanning jobs (one per FileJob). The unique names are saved + * in jobTriggerNameSet. + */ + private String buildJobTriggerName( + String fileBasename) { + // Name w/o collisions will be prefix + _ + filename (with '.' of filename replaced with '_') + // For example: JobInitializationPlugin_jobInitializer_myjobs_xml + String jobTriggerName = JOB_INITIALIZATION_PLUGIN_NAME + '_' + getName() + '_' + fileBasename.replace('.', '_'); + + // If name is too long (DB column is 80 chars), then truncate to max length + if (jobTriggerName.length() > MAX_JOB_TRIGGER_NAME_LEN) { + jobTriggerName = jobTriggerName.substring(0, MAX_JOB_TRIGGER_NAME_LEN); + } + + // Make sure this name is unique in case the same file name under different + // directories is being checked, or had a naming collision due to length truncation. + // If there is a conflict, keep incrementing a _# suffix on the name (being sure + // not to get too long), until we find a unique name. + int currentIndex = 1; + while (jobTriggerNameSet.add(jobTriggerName) == false) { + // If not our first time through, then strip off old numeric suffix + if (currentIndex > 1) { + jobTriggerName = jobTriggerName.substring(0, jobTriggerName.lastIndexOf('_')); + } + + String numericSuffix = "_" + currentIndex++; + + // If the numeric suffix would make the name too long, then make room for it. + if (jobTriggerName.length() > (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length())) { + jobTriggerName = jobTriggerName.substring(0, (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length())); + } + + jobTriggerName += numericSuffix; + } + + return jobTriggerName; + } + + /** + * Overriden to ignore wrapInUserTransaction because shutdown() + * does not interact with the Scheduler. + */ + @Override + public void shutdown() { + // Since we have nothing to do, override base shutdown so don't + // get extranious UserTransactions. + } + + private void processFile(JobFile jobFile) { + if (jobFile == null || !jobFile.getFileFound()) { + return; + } + + + try { + XMLSchedulingDataProcessor processor = + new XMLSchedulingDataProcessor(this.classLoadHelper); + + processor.addJobGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME); + processor.addTriggerGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME); + + processor.processFileAndScheduleJobs( + jobFile.getFileName(), + jobFile.getFileName(), // systemId + getScheduler()); + } catch (Exception e) { + getLog().error("Error scheduling jobs: " + e.getMessage(), e); + } + } + + public void processFile(String filePath) { + processFile((JobFile)jobFiles.get(filePath)); + } + + /** + * @see org.quartz.jobs.FileScanListener#fileUpdated(java.lang.String) + */ + public void fileUpdated(String fileName) { + if (started) { + processFile(fileName); + } + } + + class JobFile { + private String fileName; + + // These are set by initialize() + private String filePath; + private String fileBasename; + private boolean fileFound; + + protected JobFile(String fileName) throws SchedulerException { + this.fileName = fileName; + initialize(); + } + + protected String getFileName() { + return fileName; + } + + protected boolean getFileFound() { + return fileFound; + } + + protected String getFilePath() { + return filePath; + } + + protected String getFileBasename() { + return fileBasename; + } + + private void initialize() throws SchedulerException { + InputStream f = null; + try { + String furl = null; + + File file = new File(getFileName()); // files in filesystem + if (!file.exists()) { + URL url = classLoadHelper.getResource(getFileName()); + if(url != null) { + try { + furl = URLDecoder.decode(url.getPath(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + furl = url.getPath(); + } + file = new File(furl); + try { + f = url.openStream(); + } catch (IOException ignor) { + // Swallow the exception + } + } + } else { + try { + f = new java.io.FileInputStream(file); + }catch (FileNotFoundException e) { + // ignore + } + } + + if (f == null) { + if (isFailOnFileNotFound()) { + throw new SchedulerException( + "File named '" + getFileName() + "' does not exist."); + } else { + getLog().warn("File named '" + getFileName() + "' does not exist."); + } + } else { + fileFound = true; + } + filePath = (furl != null) ? furl : file.getAbsolutePath(); + fileBasename = file.getName(); + } finally { + try { + if (f != null) { + f.close(); + } + } catch (IOException ioe) { + getLog().warn("Error closing jobs file " + getFileName(), ioe); + } + } + } + } +} + +// EOF Index: 3rdParty_sources/quartz/org/quartz/quartz.properties =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/quartz.properties (.../quartz.properties) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/quartz.properties (.../quartz.properties) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -3,17 +3,17 @@ # properties file is not explicitly specified. # -org.quartz.scheduler.instanceName = DefaultQuartzScheduler -org.quartz.scheduler.rmi.export = false -org.quartz.scheduler.rmi.proxy = false -org.quartz.scheduler.wrapJobExecutionInUserTransaction = false +org.quartz.scheduler.instanceName: DefaultQuartzScheduler +org.quartz.scheduler.rmi.export: false +org.quartz.scheduler.rmi.proxy: false +org.quartz.scheduler.wrapJobExecutionInUserTransaction: false -org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool -org.quartz.threadPool.threadCount = 10 -org.quartz.threadPool.threadPriority = 5 -org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true +org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool +org.quartz.threadPool.threadCount: 10 +org.quartz.threadPool.threadPriority: 5 +org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true -org.quartz.jobStore.misfireThreshold = 60000 +org.quartz.jobStore.misfireThreshold: 60000 -org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore +org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore Index: 3rdParty_sources/quartz/org/quartz/simpl/CascadingClassLoadHelper.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/CascadingClassLoadHelper.java (.../CascadingClassLoadHelper.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/CascadingClassLoadHelper.java (.../CascadingClassLoadHelper.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.simpl; import java.util.Iterator; @@ -31,7 +28,7 @@ * A 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 perfomance). + * 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 @@ -49,6 +46,7 @@ * @see org.quartz.simpl.InitThreadContextClassLoadHelper * * @author jhouse + * @author pl47ypus */ public class CascadingClassLoadHelper implements ClassLoadHelper { @@ -61,7 +59,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private LinkedList loadHelpers; + private LinkedList loadHelpers; private ClassLoadHelper bestCandidate; @@ -75,60 +73,71 @@ /** * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling + * 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 = 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(); + for(ClassLoadHelper loadHelper: loadHelpers) { loadHelper.initialize(); } } /** * Return the class with the given name. */ - public Class loadClass(String name) throws ClassNotFoundException { + public Class loadClass(String name) throws ClassNotFoundException { if (bestCandidate != null) { try { return bestCandidate.loadClass(name); - } catch (Exception e) { + } catch (Throwable t) { bestCandidate = null; } } - ClassNotFoundException cnfe = null; - Class clazz = null; + Throwable throwable = null; + Class clazz = null; ClassLoadHelper loadHelper = null; - Iterator iter = loadHelpers.iterator(); + Iterator iter = loadHelpers.iterator(); while (iter.hasNext()) { - loadHelper = (ClassLoadHelper) iter.next(); + loadHelper = iter.next(); try { clazz = loadHelper.loadClass(name); break; - } catch (ClassNotFoundException e) { - cnfe = e; + } catch (Throwable t) { + throwable = t; } } - if (clazz == null) throw cnfe; + if (clazz == null) { + if (throwable instanceof ClassNotFoundException) { + throw (ClassNotFoundException)throwable; + } + else { + throw new ClassNotFoundException( String.format( "Unable to load class %s by any known loaders.", name), throwable); + } + } bestCandidate = loadHelper; return clazz; } + @SuppressWarnings("unchecked") + public Class loadClass(String name, Class clazz) + throws ClassNotFoundException { + return (Class) loadClass(name); + } + /** * Finds a resource with a given name. This method returns null if no * resource with this name is found. @@ -137,20 +146,23 @@ */ public URL getResource(String name) { + URL result = null; + if (bestCandidate != null) { - try { - return bestCandidate.getResource(name); - } catch (Exception e) { - bestCandidate = null; + result = bestCandidate.getResource(name); + if(result == null) { + bestCandidate = null; } + else { + return result; + } } - URL result = null; ClassLoadHelper loadHelper = null; - Iterator iter = loadHelpers.iterator(); + Iterator iter = loadHelpers.iterator(); while (iter.hasNext()) { - loadHelper = (ClassLoadHelper) iter.next(); + loadHelper = iter.next(); result = loadHelper.getResource(name); if (result != null) { @@ -160,7 +172,6 @@ bestCandidate = loadHelper; return result; - } /** @@ -171,20 +182,23 @@ */ public InputStream getResourceAsStream(String name) { + InputStream result = null; + if (bestCandidate != null) { - try { - return bestCandidate.getResourceAsStream(name); - } catch (Exception e) { + result = bestCandidate.getResourceAsStream(name); + if(result == null) { bestCandidate = null; } + else { + return result; + } } - InputStream result = null; ClassLoadHelper loadHelper = null; - Iterator iter = loadHelpers.iterator(); + Iterator iter = loadHelpers.iterator(); while (iter.hasNext()) { - loadHelper = (ClassLoadHelper) iter.next(); + loadHelper = iter.next(); result = loadHelper.getResourceAsStream(name); if (result != null) { @@ -194,7 +208,17 @@ bestCandidate = loadHelper; return result; + } + /** + * Enable sharing of the "best" class-loader with 3rd party. + * + * @return the class-loader user be the helper. + */ + public ClassLoader getClassLoader() { + return (this.bestCandidate == null) ? + Thread.currentThread().getContextClassLoader() : + this.bestCandidate.getClassLoader(); } } Index: 3rdParty_sources/quartz/org/quartz/simpl/HostnameInstanceIdGenerator.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/HostnameInstanceIdGenerator.java (.../HostnameInstanceIdGenerator.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/HostnameInstanceIdGenerator.java (.../HostnameInstanceIdGenerator.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -37,13 +37,11 @@ * @see InstanceIdGenerator * @see SimpleInstanceIdGenerator */ -public class HostnameInstanceIdGenerator implements InstanceIdGenerator -{ +public class HostnameInstanceIdGenerator implements InstanceIdGenerator { public String generateInstanceId() throws SchedulerException { try { return InetAddress.getLocalHost().getHostName(); - } - catch (Exception e) { + } catch (Exception e) { throw new SchedulerException("Couldn't get host name!", e); } } Index: 3rdParty_sources/quartz/org/quartz/simpl/InitThreadContextClassLoadHelper.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/InitThreadContextClassLoadHelper.java (.../InitThreadContextClassLoadHelper.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/InitThreadContextClassLoadHelper.java (.../InitThreadContextClassLoadHelper.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.simpl; import org.quartz.spi.ClassLoadHelper; @@ -36,6 +33,7 @@ * @see org.quartz.simpl.LoadingLoaderClassLoadHelper * * @author jhouse + * @author pl47ypus */ public class InitThreadContextClassLoadHelper implements ClassLoadHelper { @@ -60,7 +58,7 @@ /** * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling + * including the opportunity to "steal" the class loader off of the calling * thread, which is the thread that is initializing Quartz. */ public void initialize() { @@ -70,10 +68,16 @@ /** * Return the class with the given name. */ - public Class loadClass(String name) throws ClassNotFoundException { + public Class loadClass(String name) throws ClassNotFoundException { return initClassLoader.loadClass(name); } + @SuppressWarnings("unchecked") + public Class loadClass(String name, Class clazz) + throws ClassNotFoundException { + return (Class) loadClass(name); + } + /** * Finds a resource with a given name. This method returns null if no * resource with this name is found. @@ -93,4 +97,13 @@ public InputStream getResourceAsStream(String name) { return initClassLoader.getResourceAsStream(name); } + + /** + * Enable sharing of the class-loader with 3rd party. + * + * @return the class-loader user be the helper. + */ + public ClassLoader getClassLoader() { + return this.initClassLoader; + } } Index: 3rdParty_sources/quartz/org/quartz/simpl/LoadingLoaderClassLoadHelper.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/LoadingLoaderClassLoadHelper.java (.../LoadingLoaderClassLoadHelper.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/LoadingLoaderClassLoadHelper.java (.../LoadingLoaderClassLoadHelper.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.simpl; import org.quartz.spi.ClassLoadHelper; @@ -35,6 +32,7 @@ * @see org.quartz.simpl.CascadingClassLoadHelper * * @author jhouse + * @author pl47ypus */ public class LoadingLoaderClassLoadHelper implements ClassLoadHelper { @@ -48,7 +46,7 @@ /** * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling + * including the opportunity to "steal" the class loader off of the calling * thread, which is the thread that is initializing Quartz. */ public void initialize() { @@ -57,10 +55,16 @@ /** * Return the class with the given name. */ - public Class loadClass(String name) throws ClassNotFoundException { + public Class loadClass(String name) throws ClassNotFoundException { return getClassLoader().loadClass(name); } + @SuppressWarnings("unchecked") + public Class loadClass(String name, Class clazz) + throws ClassNotFoundException { + return (Class) loadClass(name); + } + /** * Finds a resource with a given name. This method returns null if no * resource with this name is found. @@ -81,7 +85,12 @@ return getClassLoader().getResourceAsStream(name); } - private ClassLoader getClassLoader() { + /** + * Enable sharing of the class-loader with 3rd party. + * + * @return the class-loader user be the helper. + */ + public ClassLoader getClassLoader() { return this.getClass().getClassLoader(); } } Index: 3rdParty_sources/quartz/org/quartz/simpl/PropertySettingJobFactory.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/PropertySettingJobFactory.java (.../PropertySettingJobFactory.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/PropertySettingJobFactory.java (.../PropertySettingJobFactory.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -21,12 +21,14 @@ 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 org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.quartz.Job; import org.quartz.JobDataMap; +import org.quartz.Scheduler; +import org.quartz.SchedulerContext; import org.quartz.SchedulerException; import org.quartz.spi.TriggerFiredBundle; @@ -35,29 +37,39 @@ /** * 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. + * then attempts to set all values from the SchedulerContext and + * the JobExecutionContext's merged JobDataMap onto + * bean properties of the Job. * + *

Set the warnIfPropertyNotFound property to true if you'd like noisy logging in + * the case of values in the JobDataMap not mapping to properties on your Job + * class. This may be useful for troubleshooting typos of property names, etc. + * but very noisy if you regularly (and purposely) have extra things in your + * JobDataMap.

+ * + *

Also of possible interest is the throwIfPropertyNotFound property which + * will throw exceptions on unmatched JobDataMap keys.

+ * * @see org.quartz.spi.JobFactory * @see SimpleJobFactory - * @see JobExecutionContext#getMergedJobDataMap() + * @see SchedulerContext + * @see org.quartz.JobExecutionContext#getMergedJobDataMap() * @see #setWarnIfPropertyNotFound(boolean) * @see #setThrowIfPropertyNotFound(boolean) * * @author jhouse */ public class PropertySettingJobFactory extends SimpleJobFactory { - - private Log log = LogFactory.getLog(SimpleJobFactory.class); - - private boolean warnIfNotFound = true; + private boolean warnIfNotFound = false; private boolean throwIfNotFound = false; - public Job newJob(TriggerFiredBundle bundle) throws SchedulerException { + @Override + public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException { - Job job = super.newJob(bundle); + Job job = super.newJob(bundle, scheduler); JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.putAll(scheduler.getContext()); jobDataMap.putAll(bundle.getJobDetail().getJobDataMap()); jobDataMap.putAll(bundle.getTrigger().getJobDataMap()); @@ -71,187 +83,175 @@ BeanInfo bi = null; try { bi = Introspector.getBeanInfo(obj.getClass()); - } catch (IntrospectionException e1) { - if(isThrowIfPropertyNotFound()) { - throw new SchedulerException("Unable to introspect Job class.", e1); - } - if(isWarnIfPropertyNotFound()) { - log.warn("Unable to introspect Job class.", e1); - } + } catch (IntrospectionException e) { + handleError("Unable to introspect Job class.", e); } PropertyDescriptor[] propDescs = bi.getPropertyDescriptors(); - java.util.Iterator keys = data.keySet().iterator(); - while (keys.hasNext()) { - String name = (String) keys.next(); + // 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; + Class paramType = null; Object o = null; try { if (setMeth == null) { - if(isThrowIfPropertyNotFound()) { - throw new SchedulerException( - "No setter on Job class " + obj.getClass() + - " for property '" + name + "'"); - } - if(isWarnIfPropertyNotFound()) { - log.warn( - "No setter on Job class " + obj.getClass() + - " for property '" + name + "'"); - } + handleError( + "No setter on Job class " + obj.getClass().getName() + + " for property '" + name + "'"); continue; } paramType = setMeth.getParameterTypes()[0]; - o = data.get(name); + o = entry.getValue(); - if (paramType.equals(int.class)) { - if(o instanceof Integer) - setMeth.invoke(obj, - new Object[]{o}); - else if(o instanceof String) - setMeth.invoke(obj, - new Object[]{data.getIntegerFromString(name)}); - } else if (paramType.equals(long.class)) { - if(o instanceof Long) - setMeth.invoke(obj, - new Object[]{o}); - else if(o instanceof String) - setMeth.invoke(obj, - new Object[]{data.getLongFromString(name)}); - } else if (paramType.equals(float.class)) { - if(o instanceof Float) - setMeth.invoke(obj, - new Object[]{o}); - else if(o instanceof String) - setMeth.invoke(obj, - new Object[]{data.getFloatFromString(name)}); - } else if (paramType.equals(double.class)) { - if(o instanceof Double) - setMeth.invoke(obj, - new Object[]{o}); - else if(o instanceof String) - setMeth.invoke(obj, - new Object[]{data.getDoubleFromString(name)}); - } else if (paramType.equals(boolean.class)) { - if(o instanceof Boolean) - setMeth.invoke(obj, - new Object[]{o}); - else if(o instanceof String) - setMeth.invoke(obj, - new Object[]{data.getBooleanFromString(name)}); - } else if (paramType.equals(String.class)) { - if(o instanceof String) - setMeth.invoke(obj, - new Object[]{o}); - } else { - if(paramType.isAssignableFrom(o.getClass())) { - setMeth.invoke(obj, - new Object[]{o}); + Object parm = null; + if (paramType.isPrimitive()) { + if (o == null) { + handleError( + "Cannot set primitive property '" + name + + "' on Job class " + obj.getClass().getName() + + " to null."); + continue; } - else - throw new NoSuchMethodException(); + + if (paramType.equals(int.class)) { + if (o instanceof String) { + parm = Integer.valueOf((String)o); + } else if (o instanceof Integer) { + parm = o; + } + } else if (paramType.equals(long.class)) { + if (o instanceof String) { + parm = Long.valueOf((String)o); + } else if (o instanceof Long) { + parm = o; + } + } else if (paramType.equals(float.class)) { + if (o instanceof String) { + parm = Float.valueOf((String)o); + } else if (o instanceof Float) { + parm = o; + } + } else if (paramType.equals(double.class)) { + if (o instanceof String) { + parm = Double.valueOf((String)o); + } else if (o instanceof Double) { + parm = o; + } + } else if (paramType.equals(boolean.class)) { + if (o instanceof String) { + parm = Boolean.valueOf((String)o); + } else if (o instanceof Boolean) { + parm = o; + } + } else if (paramType.equals(byte.class)) { + if (o instanceof String) { + parm = Byte.valueOf((String)o); + } else if (o instanceof Byte) { + parm = o; + } + } else if (paramType.equals(short.class)) { + if (o instanceof String) { + parm = Short.valueOf((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 = Character.valueOf(str.charAt(0)); + } + } else if (o instanceof Character) { + parm = o; + } + } + } else if ((o != null) && (paramType.isAssignableFrom(o.getClass()))) { + parm = o; } - } catch (NumberFormatException nfe) { - if(isThrowIfPropertyNotFound()) { - throw new SchedulerException( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' expects a " + paramType + - " but was given " + o, nfe); + + // 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; } - if(isWarnIfPropertyNotFound()) { - log.warn( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' expects a " + paramType + - " but was given " + o, nfe); - } - continue; - } catch (NoSuchMethodException e) { - if(isThrowIfPropertyNotFound()) { - throw new SchedulerException( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' expects a " + paramType + - " but was given " + o.getClass()); - } - if(isWarnIfPropertyNotFound()) { - log.warn( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' expects a " + paramType + - " but was given a " + o.getClass()); - } - 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) { - if(isThrowIfPropertyNotFound()) { - throw new SchedulerException( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' expects a " + paramType + - " but was given " + o.getClass(),e ); - } - if(isWarnIfPropertyNotFound()) { - log.warn( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' expects a " + paramType + - " but was given a " + o.getClass(), e); - } - continue; + 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) { - if(isThrowIfPropertyNotFound()) { - throw new SchedulerException( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' could not be accessed.", e); - } - if(isWarnIfPropertyNotFound()) { - log.warn( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' expects a " + paramType + - "' could not be accessed.", e); - } - continue; + handleError( + "The setter on Job class " + obj.getClass().getName() + + " for property '" + name + + "' could not be accessed.", e); } catch (InvocationTargetException e) { - if(isThrowIfPropertyNotFound()) { - throw new SchedulerException( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' could not be accessed.", e); - } - if(isWarnIfPropertyNotFound()) { - log.warn( - "The setter on Job class " + obj.getClass() + - " for property '" + name + - "' expects a " + paramType + - "' could not be accessed.", e); - } - continue; + 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) + if(wMeth == null) { continue; + } - if(wMeth.getParameterTypes().length != 1) + if(wMeth.getParameterTypes().length != 1) { continue; + } - if (wMeth.getName().equals(name)) return wMeth; + if (wMeth.getName().equals(name)) { + return wMeth; + } } return null; @@ -300,6 +300,4 @@ public void setWarnIfPropertyNotFound(boolean warnIfNotFound) { this.warnIfNotFound = warnIfNotFound; } - - } \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/simpl/RAMJobStore.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/RAMJobStore.java (.../RAMJobStore.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/RAMJobStore.java (.../RAMJobStore.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,35 +15,50 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.simpl; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicLong; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.quartz.Calendar; +import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; +import org.quartz.JobKey; import org.quartz.JobPersistenceException; import org.quartz.ObjectAlreadyExistsException; -import org.quartz.SchedulerException; import org.quartz.Trigger; -import org.quartz.core.SchedulingContext; +import org.quartz.TriggerKey; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.Trigger.TriggerState; +import org.quartz.Trigger.TriggerTimeComparator; +import org.quartz.impl.JobDetailImpl; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.impl.matchers.StringMatcher; import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.JobStore; +import org.quartz.spi.OperableTrigger; import org.quartz.spi.SchedulerSignaler; import org.quartz.spi.TriggerFiredBundle; +import org.quartz.spi.TriggerFiredResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import static org.quartz.impl.matchers.EverythingMatcher.allTriggers; + /** *

* This class implements a {@link org.quartz.spi.JobStore} that @@ -59,6 +74,7 @@ * * @author James House * @author Sharada Jambula + * @author Eric Mueller */ public class RAMJobStore implements JobStore { @@ -70,32 +86,34 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - protected HashMap jobsByFQN = new HashMap(1000); + protected HashMap jobsByKey = new HashMap(1000); - protected HashMap triggersByFQN = new HashMap(1000); + protected HashMap triggersByKey = new HashMap(1000); - protected HashMap jobsByGroup = new HashMap(25); + protected HashMap> jobsByGroup = new HashMap>(25); - protected HashMap triggersByGroup = new HashMap(25); + protected HashMap> triggersByGroup = new HashMap>(25); - protected TreeSet timeTriggers = new TreeSet(new TriggerComparator()); + protected TreeSet timeTriggers = new TreeSet(new TriggerWrapperComparator()); - protected HashMap calendarsByName = new HashMap(25); + protected HashMap calendarsByName = new HashMap(25); - protected ArrayList triggers = new ArrayList(1000); + protected ArrayList triggers = new ArrayList(1000); - protected Object jobLock = new Object(); + protected final Object lock = new Object(); - protected Object triggerLock = new Object(); + protected HashSet pausedTriggerGroups = new HashSet(); - protected HashSet pausedTriggerGroups = new HashSet(); + protected HashSet pausedJobGroups = new HashSet(); - protected HashSet blockedJobs = new HashSet(); + protected HashSet blockedJobs = new HashSet(); protected long misfireThreshold = 5000l; protected SchedulerSignaler signaler; + private final Logger log = LoggerFactory.getLogger(getClass()); + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -120,8 +138,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - protected Log getLog() { - return LogFactory.getLog(RAMJobStore.class); + protected Logger getLog() { + return log; } /** @@ -130,34 +148,41 @@ * used, in order to give the it a chance to initialize. *

*/ - public void initialize(ClassLoadHelper loadHelper, - SchedulerSignaler signaler) { + public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler schedSignaler) { - this.signaler = signaler; + this.signaler = schedSignaler; getLog().info("RAMJobStore initialized."); } - public void schedulerStarted() throws SchedulerException - { + public void schedulerStarted() { // nothing to do } + public void schedulerPaused() { + // nothing to do + } + + public void schedulerResumed() { + // nothing to do + } + public long getMisfireThreshold() { return misfireThreshold; } /** - * The the number of milliseconds by which a trigger must have missed its + * The number of milliseconds by which a trigger must have missed its * next-fire-time, in order for it to be considered "misfired" and thus * have its misfire instruction applied. * - * @param misfireThreshold + * @param misfireThreshold the new misfire threshold */ + @SuppressWarnings("UnusedDeclaration") public void setMisfireThreshold(long misfireThreshold) { - if (misfireThreshold < 1) - throw new IllegalArgumentException( - "Misfirethreashold must be larger than 0"); + if (misfireThreshold < 1) { + throw new IllegalArgumentException("Misfire threshold must be larger than 0"); + } this.misfireThreshold = misfireThreshold; } @@ -176,6 +201,39 @@ } /** + * Clear (delete!) all scheduling data - all {@link Job}s, {@link Trigger}s + * {@link Calendar}s. + * + * @throws JobPersistenceException + */ + public void clearAllSchedulingData() throws JobPersistenceException { + + synchronized (lock) { + // unschedule jobs (delete triggers) + List lst = getTriggerGroupNames(); + for (String group: lst) { + Set keys = getTriggerKeys(GroupMatcher.triggerGroupEquals(group)); + for (TriggerKey key: keys) { + removeTrigger(key); + } + } + // delete jobs + lst = getJobGroupNames(); + for (String group: lst) { + Set keys = getJobKeys(GroupMatcher.jobGroupEquals(group)); + for (JobKey key: keys) { + removeJob(key); + } + } + // delete calendars + lst = getCalendarNames(); + for(String name: lst) { + removeCalendar(name); + } + } + } + + /** *

* Store the given {@link org.quartz.JobDetail} and {@link org.quartz.Trigger}. *

@@ -188,10 +246,10 @@ * 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); + public void storeJobAndTrigger(JobDetail newJob, + OperableTrigger newTrigger) throws JobPersistenceException { + storeJob(newJob, false); + storeTrigger(newTrigger, false); } /** @@ -209,34 +267,35 @@ * if a Job with the same name/group already * exists, and replaceExisting is set to false. */ - public void storeJob(SchedulingContext ctxt, JobDetail newJob, + public void storeJob(JobDetail newJob, boolean replaceExisting) throws ObjectAlreadyExistsException { - JobWrapper jw = new JobWrapper(newJob); + JobWrapper jw = new JobWrapper((JobDetail)newJob.clone()); boolean repl = false; - if (jobsByFQN.get(jw.key) != null) { - if (!replaceExisting) + synchronized (lock) { + if (jobsByKey.get(jw.key) != null) { + if (!replaceExisting) { throw new ObjectAlreadyExistsException(newJob); - repl = true; - } + } + repl = true; + } - synchronized (jobLock) { if (!repl) { // get job group - HashMap grpMap = (HashMap) jobsByGroup.get(newJob.getGroup()); + HashMap grpMap = jobsByGroup.get(newJob.getKey().getGroup()); if (grpMap == null) { - grpMap = new HashMap(100); - jobsByGroup.put(newJob.getGroup(), grpMap); + grpMap = new HashMap(100); + jobsByGroup.put(newJob.getKey().getGroup(), grpMap); } // add to jobs by group - grpMap.put(newJob.getName(), jw); + grpMap.put(newJob.getKey(), jw); // add to jobs by FQN map - jobsByFQN.put(jw.key, jw); + jobsByKey.put(jw.key, jw); } else { // update job detail - JobWrapper orig = (JobWrapper) jobsByFQN.get(jw.key); - orig.jobDetail = newJob; + JobWrapper orig = jobsByKey.get(jw.key); + orig.jobDetail = jw.jobDetail; // already cloned } } } @@ -247,47 +306,93 @@ * name, and any {@link org.quartz.Trigger} s that reference * it. *

- * - * @param jobName - * The name of the 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); + public boolean removeJob(JobKey jobKey) { 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 (jobLock) { - found = (jobsByFQN.remove(key) != null) | found; + synchronized (lock) { + List triggersOfJob = getTriggersForJob(jobKey); + for (OperableTrigger trig: triggersOfJob) { + this.removeTrigger(trig.getKey()); + found = true; + } + + found = (jobsByKey.remove(jobKey) != null) | found; if (found) { - HashMap grpMap = (HashMap) jobsByGroup.get(groupName); + HashMap grpMap = jobsByGroup.get(jobKey.getGroup()); if (grpMap != null) { - grpMap.remove(jobName); - if (grpMap.size() == 0) jobsByGroup.remove(groupName); + grpMap.remove(jobKey); + if (grpMap.size() == 0) { + jobsByGroup.remove(jobKey.getGroup()); + } } } } return found; } + public boolean removeJobs(List jobKeys) + throws JobPersistenceException { + boolean allFound = true; + + synchronized (lock) { + for(JobKey key: jobKeys) + allFound = removeJob(key) && allFound; + } + + return allFound; + } + + public boolean removeTriggers(List triggerKeys) + throws JobPersistenceException { + boolean allFound = true; + + synchronized (lock) { + for(TriggerKey key: triggerKeys) + allFound = removeTrigger(key) && allFound; + } + + return allFound; + } + + public void storeJobsAndTriggers( + Map> triggersAndJobs, boolean replace) + throws JobPersistenceException { + + synchronized (lock) { + // make sure there are no collisions... + if(!replace) { + for(Entry> e: triggersAndJobs.entrySet()) { + if(checkExists(e.getKey().getKey())) + throw new ObjectAlreadyExistsException(e.getKey()); + for(Trigger trigger: e.getValue()) { + if(checkExists(trigger.getKey())) + throw new ObjectAlreadyExistsException(trigger); + } + } + } + // do bulk add... + for(Entry> e: triggersAndJobs.entrySet()) { + storeJob(e.getKey(), true); + for(Trigger trigger: e.getValue()) { + storeTrigger((OperableTrigger) trigger, true); + } + } + } + + } + /** *

* Store the given {@link org.quartz.Trigger}. *

- * + * * @param newTrigger * The Trigger to be stored. * @param replaceExisting @@ -297,49 +402,50 @@ * @throws ObjectAlreadyExistsException * if a Trigger with the same name/group already * exists, and replaceExisting is set to false. - * - * @see #pauseTriggerGroup(SchedulingContext, String) + * + * @see #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) */ - public void storeTrigger(SchedulingContext ctxt, Trigger newTrigger, + public void storeTrigger(OperableTrigger newTrigger, boolean replaceExisting) throws JobPersistenceException { - TriggerWrapper tw = new TriggerWrapper(newTrigger); + TriggerWrapper tw = new TriggerWrapper((OperableTrigger)newTrigger.clone()); - if (triggersByFQN.get(tw.key) != null) { - if (!replaceExisting) + synchronized (lock) { + if (triggersByKey.get(tw.key) != null) { + if (!replaceExisting) { throw new ObjectAlreadyExistsException(newTrigger); - - removeTrigger(ctxt, newTrigger.getName(), newTrigger.getGroup()); - } - - if (retrieveJob(ctxt, newTrigger.getJobName(), newTrigger.getJobGroup()) == null) + } + + removeTrigger(newTrigger.getKey(), false); + } + + if (retrieveJob(newTrigger.getJobKey()) == null) { throw new JobPersistenceException("The job (" - + newTrigger.getFullJobName() + + newTrigger.getJobKey() + ") 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()); + HashMap grpMap = triggersByGroup.get(newTrigger.getKey().getGroup()); if (grpMap == null) { - grpMap = new HashMap(100); - triggersByGroup.put(newTrigger.getGroup(), grpMap); + grpMap = new HashMap(100); + triggersByGroup.put(newTrigger.getKey().getGroup(), grpMap); } - grpMap.put(newTrigger.getName(), tw); + grpMap.put(newTrigger.getKey(), tw); // add to triggers by FQN map - triggersByFQN.put(tw.key, tw); + triggersByKey.put(tw.key, tw); - synchronized (pausedTriggerGroups) { - if (pausedTriggerGroups.contains(newTrigger.getGroup())) { - tw.state = TriggerWrapper.STATE_PAUSED; - if (blockedJobs.contains(tw.jobKey)) - tw.state = TriggerWrapper.STATE_PAUSED_BLOCKED; + if (pausedTriggerGroups.contains(newTrigger.getKey().getGroup()) + || pausedJobGroups.contains(newTrigger.getJobKey().getGroup())) { + 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); + } else if (blockedJobs.contains(tw.jobKey)) { + tw.state = TriggerWrapper.STATE_BLOCKED; + } else { + timeTriggers.add(tw); } } } @@ -349,191 +455,219 @@ * Remove (delete) the {@link org.quartz.Trigger} with the * given name. *

- * - * @param triggerName - * The name of the 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) { - String key = TriggerWrapper.getTriggerNameKey(triggerName, groupName); + public boolean removeTrigger(TriggerKey triggerKey) { + return removeTrigger(triggerKey, true); + } + + private boolean removeTrigger(TriggerKey key, boolean removeOrphanedJob) { - boolean found = false; + boolean found; - synchronized (triggerLock) { + synchronized (lock) { // remove from triggers by FQN map - found = (triggersByFQN.remove(key) == null) ? false : true; + found = (triggersByKey.remove(key) != null); if (found) { TriggerWrapper tw = null; // remove from triggers by group - HashMap grpMap = (HashMap) triggersByGroup.get(groupName); + HashMap grpMap = triggersByGroup.get(key.getGroup()); if (grpMap != null) { - grpMap.remove(triggerName); - if (grpMap.size() == 0) triggersByGroup.remove(groupName); + grpMap.remove(key); + if (grpMap.size() == 0) { + triggersByGroup.remove(key.getGroup()); + } } // remove from triggers array - Iterator tgs = triggers.iterator(); + Iterator tgs = triggers.iterator(); while (tgs.hasNext()) { - tw = (TriggerWrapper) tgs.next(); + tw = tgs.next(); if (key.equals(tw.key)) { tgs.remove(); break; } } timeTriggers.remove(tw); - 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()); + if (removeOrphanedJob) { + JobWrapper jw = jobsByKey.get(tw.jobKey); + List trigs = getTriggersForJob(tw.jobKey); + if ((trigs == null || trigs.size() == 0) && !jw.jobDetail.isDurable()) { + if (removeJob(jw.key)) { + signaler.notifySchedulerListenersJobDeleted(jw.key); + } + } + } } } return found; } - /** - * @see org.quartz.spi.JobStore#replaceTrigger(org.quartz.core.SchedulingContext, java.lang.String, java.lang.String, org.quartz.Trigger) + /** + * @see org.quartz.spi.JobStore#replaceTrigger(TriggerKey triggerKey, OperableTrigger newTrigger) */ - public boolean replaceTrigger(SchedulingContext ctxt, String triggerName, String groupName, Trigger newTrigger) throws JobPersistenceException { - String key = TriggerWrapper.getTriggerNameKey(triggerName, groupName); + public boolean replaceTrigger(TriggerKey triggerKey, OperableTrigger newTrigger) throws JobPersistenceException { - boolean found = false; + boolean found; - synchronized (triggerLock) { + synchronized (lock) { // remove from triggers by FQN map - TriggerWrapper tw = (TriggerWrapper) triggersByFQN.remove(key); - found = ( tw == null) ? false : true; - + TriggerWrapper tw = triggersByKey.remove(triggerKey); + found = (tw != null); + if (found) { - - if(!tw.getTrigger().getJobName().equals(newTrigger.getJobName()) || - !tw.getTrigger().getJobGroup().equals(newTrigger.getJobGroup())) + + if (!tw.getTrigger().getJobKey().equals(newTrigger.getJobKey())) { 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); + HashMap grpMap = triggersByGroup.get(triggerKey.getGroup()); if (grpMap != null) { - grpMap.remove(triggerName); - if (grpMap.size() == 0) triggersByGroup.remove(groupName); + grpMap.remove(triggerKey); + if (grpMap.size() == 0) { + triggersByGroup.remove(triggerKey.getGroup()); + } } // remove from triggers array - Iterator tgs = triggers.iterator(); + Iterator tgs = triggers.iterator(); while (tgs.hasNext()) { - tw = (TriggerWrapper) tgs.next(); - if (key.equals(tw.key)) { + tw = tgs.next(); + if (triggerKey.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... + storeTrigger(newTrigger, false); + } catch(JobPersistenceException jpe) { + storeTrigger(tw.getTrigger(), false); // put previous trigger back... throw jpe; } } } return found; } - + /** *

* Retrieve the {@link org.quartz.JobDetail} for the given * {@link org.quartz.Job}. *

- * - * @param jobName - * The name of the 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)); - if (jw != null) return jw.jobDetail; - - return null; + public JobDetail retrieveJob(JobKey jobKey) { + synchronized(lock) { + JobWrapper jw = jobsByKey.get(jobKey); + return (jw != null) ? (JobDetail)jw.jobDetail.clone() : null; + } } /** *

* Retrieve the given {@link org.quartz.Trigger}. *

- * - * @param jobName - * The name of the 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)); - if (tw != null) return tw.getTrigger(); - - return null; + public OperableTrigger retrieveTrigger(TriggerKey triggerKey) { + synchronized(lock) { + TriggerWrapper tw = triggersByKey.get(triggerKey); + + return (tw != null) ? (OperableTrigger)tw.getTrigger().clone() : null; + } } - + /** + * Determine whether a {@link Job} with the given identifier already + * exists within the scheduler. + * + * @param jobKey the identifier to check for + * @return true if a Job exists with the given identifier + * @throws JobPersistenceException + */ + public boolean checkExists(JobKey jobKey) throws JobPersistenceException { + synchronized(lock) { + JobWrapper jw = jobsByKey.get(jobKey); + return (jw != null); + } + } + + /** + * Determine whether a {@link Trigger} with the given identifier already + * exists within the scheduler. + * + * @param triggerKey the identifier to check for + * @return true if a Trigger exists with the given identifier + * @throws JobPersistenceException + */ + public boolean checkExists(TriggerKey triggerKey) throws JobPersistenceException { + synchronized(lock) { + TriggerWrapper tw = triggersByKey.get(triggerKey); + + return (tw != null); + } + } + + /** *

* Get the current state of the identified {@link Trigger}. *

- * - * @see Trigger#STATE_NORMAL - * @see Trigger#STATE_PAUSED - * @see Trigger#STATE_COMPLETE - * @see Trigger#STATE_ERROR - * @see Trigger#STATE_BLOCKED - * @see Trigger#STATE_NONE + * + * @see TriggerState#NORMAL + * @see TriggerState#PAUSED + * @see TriggerState#COMPLETE + * @see TriggerState#ERROR + * @see TriggerState#BLOCKED + * @see TriggerState#NONE */ - public int getTriggerState(SchedulingContext ctxt, String triggerName, - String groupName) throws JobPersistenceException { - TriggerWrapper tw = (TriggerWrapper) triggersByFQN.get(TriggerWrapper - .getTriggerNameKey(triggerName, groupName)); - if (tw == null) return Trigger.STATE_NONE; - - if (tw.state == TriggerWrapper.STATE_COMPLETE) - return Trigger.STATE_COMPLETE; - - if (tw.state == TriggerWrapper.STATE_PAUSED) - return Trigger.STATE_PAUSED; - - if (tw.state == TriggerWrapper.STATE_PAUSED_BLOCKED) - return Trigger.STATE_PAUSED; - - if (tw.state == TriggerWrapper.STATE_BLOCKED) - return Trigger.STATE_BLOCKED; - - if (tw.state == TriggerWrapper.STATE_ERROR) - return Trigger.STATE_ERROR; - - return Trigger.STATE_NORMAL; + public TriggerState getTriggerState(TriggerKey triggerKey) throws JobPersistenceException { + synchronized(lock) { + TriggerWrapper tw = triggersByKey.get(triggerKey); + + if (tw == null) { + return TriggerState.NONE; + } + + if (tw.state == TriggerWrapper.STATE_COMPLETE) { + return TriggerState.COMPLETE; + } + + if (tw.state == TriggerWrapper.STATE_PAUSED) { + return TriggerState.PAUSED; + } + + if (tw.state == TriggerWrapper.STATE_PAUSED_BLOCKED) { + return TriggerState.PAUSED; + } + + if (tw.state == TriggerWrapper.STATE_BLOCKED) { + return TriggerState.BLOCKED; + } + + if (tw.state == TriggerWrapper.STATE_ERROR) { + return TriggerState.ERROR; + } + + return TriggerState.NORMAL; + } } /** *

* Store the given {@link org.quartz.Calendar}. *

- * + * * @param calendar * The Calendar to be stored. * @param replaceExisting @@ -542,36 +676,42 @@ * should be over-written. * @param updateTriggers * If true, any Triggers existing - * in the JobStore that reference an 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, + public void storeCalendar(String name, Calendar calendar, boolean replaceExisting, boolean updateTriggers) - throws ObjectAlreadyExistsException { - Object obj = calendarsByName.get(name); + throws ObjectAlreadyExistsException { - 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(); + calendar = (Calendar) calendar.clone(); + + synchronized (lock) { + + Object obj = calendarsByName.get(name); + + if (obj != null && !replaceExisting) { + throw new ObjectAlreadyExistsException( + "Calendar with name '" + name + "' already exists."); + } else if (obj != null) { + calendarsByName.remove(name); + } + + calendarsByName.put(name, calendar); + + if(obj != null && updateTriggers) { + for (TriggerWrapper tw : getTriggerWrappersForCalendar(name)) { + OperableTrigger trig = tw.getTrigger(); boolean removed = timeTriggers.remove(tw); - + trig.updateWithNewCalendar(calendar, getMisfireThreshold()); - - if(removed) + + if (removed) { timeTriggers.add(tw); + } } } } @@ -582,32 +722,34 @@ * Remove (delete) the {@link org.quartz.Calendar} with the * given name. *

- * + * *

* If removal of the Calendar would result in - * s pointing to non-existent calendars, then a + * Triggers pointing to non-existent calendars, then a * JobPersistenceException will be thrown.

* * * @param calName The name of the 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 { + public boolean removeCalendar(String calName) + throws JobPersistenceException { int numRefs = 0; - synchronized (triggerLock) { - Iterator itr = triggers.iterator(); - while (itr.hasNext()) { - Trigger trigg = ((TriggerWrapper) itr.next()).trigger; + synchronized (lock) { + for (TriggerWrapper trigger : triggers) { + OperableTrigger trigg = trigger.trigger; if (trigg.getCalendarName() != null - && trigg.getCalendarName().equals(calName)) numRefs++; + && trigg.getCalendarName().equals(calName)) { + numRefs++; + } } } - if (numRefs > 0) - throw new JobPersistenceException( - "Calender cannot be removed if it referenced by a Trigger!"); + if (numRefs > 0) { + throw new JobPersistenceException( + "Calender cannot be removed if it referenced by a Trigger!"); + } return (calendarsByName.remove(calName) != null); } @@ -616,14 +758,19 @@ *

* Retrieve the given {@link org.quartz.Trigger}. *

- * + * * @param calName * The name of the 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); + public Calendar retrieveCalendar(String calName) { + synchronized (lock) { + Calendar cal = calendarsByName.get(calName); + if(cal != null) + return (Calendar) cal.clone(); + return null; + } } /** @@ -632,8 +779,10 @@ * stored in the JobsStore. *

*/ - public int getNumberOfJobs(SchedulingContext ctxt) { - return jobsByFQN.size(); + public int getNumberOfJobs() { + synchronized (lock) { + return jobsByKey.size(); + } } /** @@ -642,8 +791,10 @@ * stored in the JobsStore. *

*/ - public int getNumberOfTriggers(SchedulingContext ctxt) { - return triggers.size(); + public int getNumberOfTriggers() { + synchronized (lock) { + return triggers.size(); + } } /** @@ -652,78 +803,121 @@ * stored in the JobsStore. *

*/ - public int getNumberOfCalendars(SchedulingContext ctxt) { - return calendarsByName.size(); + public int getNumberOfCalendars() { + synchronized (lock) { + return calendarsByName.size(); + } } /** *

* Get the names of all of the {@link org.quartz.Job} s that - * have the given group name. + * match the given groupMatcher. *

*/ - public String[] getJobNames(SchedulingContext ctxt, String groupName) { - String[] outList = null; - HashMap grpMap = (HashMap) jobsByGroup.get(groupName); - if (grpMap != null) { - synchronized (jobLock) { - outList = new String[grpMap.size()]; - int outListPos = 0; - Iterator keys = grpMap.keySet().iterator(); - while (keys.hasNext()) { - String key = (String) keys.next(); - JobWrapper jw = (JobWrapper) grpMap.get(key); - if (jw != null) - outList[outListPos++] = jw.jobDetail.getName(); - } + public Set getJobKeys(GroupMatcher matcher) { + Set outList = null; + synchronized (lock) { + + StringMatcher.StringOperatorName operator = matcher.getCompareWithOperator(); + String compareToValue = matcher.getCompareToValue(); + + switch(operator) { + case EQUALS: + HashMap grpMap = jobsByGroup.get(compareToValue); + if (grpMap != null) { + outList = new HashSet(); + + for (JobWrapper jw : grpMap.values()) { + + if (jw != null) { + outList.add(jw.jobDetail.getKey()); + } + } + } + break; + + default: + for (Map.Entry> entry : jobsByGroup.entrySet()) { + if(operator.evaluate(entry.getKey(), compareToValue) && entry.getValue() != null) { + if(outList == null) { + outList = new HashSet(); + } + for (JobWrapper jobWrapper : entry.getValue().values()) { + if(jobWrapper != null) { + outList.add(jobWrapper.jobDetail.getKey()); + } + } + } + } } - } else - outList = new String[0]; + } - return outList; + return outList == null ? java.util.Collections.emptySet() : outList; } /** *

* Get the names of all of the {@link 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). *

*/ - public String[] getCalendarNames(SchedulingContext ctxt) { - Set names = calendarsByName.keySet(); - return (String[]) names.toArray(new String[names.size()]); + public List getCalendarNames() { + synchronized(lock) { + return new LinkedList(calendarsByName.keySet()); + } } /** *

* Get the names of all of the {@link org.quartz.Trigger} s - * that have the given group name. + * that match the given groupMatcher. *

*/ - public String[] getTriggerNames(SchedulingContext ctxt, String groupName) { - String[] outList = null; - HashMap grpMap = (HashMap) triggersByGroup.get(groupName); - if (grpMap != null) { - synchronized (triggerLock) { - outList = new String[grpMap.size()]; - int outListPos = 0; - Iterator keys = grpMap.keySet().iterator(); - while (keys.hasNext()) { - String key = (String) keys.next(); - TriggerWrapper tw = (TriggerWrapper) grpMap.get(key); - if (tw != null) - outList[outListPos++] = tw.trigger.getName(); - } + public Set getTriggerKeys(GroupMatcher matcher) { + Set outList = null; + synchronized (lock) { + + StringMatcher.StringOperatorName operator = matcher.getCompareWithOperator(); + String compareToValue = matcher.getCompareToValue(); + + switch(operator) { + case EQUALS: + HashMap grpMap = triggersByGroup.get(compareToValue); + if (grpMap != null) { + outList = new HashSet(); + + for (TriggerWrapper tw : grpMap.values()) { + + if (tw != null) { + outList.add(tw.trigger.getKey()); + } + } + } + break; + + default: + for (Map.Entry> entry : triggersByGroup.entrySet()) { + if(operator.evaluate(entry.getKey(), compareToValue) && entry.getValue() != null) { + if(outList == null) { + outList = new HashSet(); + } + for (TriggerWrapper triggerWrapper : entry.getValue().values()) { + if(triggerWrapper != null) { + outList.add(triggerWrapper.trigger.getKey()); + } + } + } + } } - } else - outList = new String[0]; + } - return outList; + return outList == null ? Collections.emptySet() : outList; } /** @@ -732,16 +926,11 @@ * groups. *

*/ - public String[] getJobGroupNames(SchedulingContext ctxt) { - String[] outList = null; + public List getJobGroupNames() { + List outList; - synchronized (jobLock) { - outList = new String[jobsByGroup.size()]; - int outListPos = 0; - Iterator keys = jobsByGroup.keySet().iterator(); - while (keys.hasNext()) { - outList[outListPos++] = (String) keys.next(); - } + synchronized (lock) { + outList = new LinkedList(jobsByGroup.keySet()); } return outList; @@ -753,16 +942,11 @@ * groups. *

*/ - public String[] getTriggerGroupNames(SchedulingContext ctxt) { - String[] outList = null; + public List getTriggerGroupNames() { + LinkedList outList; - synchronized (triggerLock) { - outList = new String[triggersByGroup.size()]; - int outListPos = 0; - Iterator keys = triggersByGroup.keySet().iterator(); - while (keys.hasNext()) { - outList[outListPos++] = (String) keys.next(); - } + synchronized (lock) { + outList = new LinkedList(triggersByGroup.keySet()); } return outList; @@ -772,119 +956,144 @@ *

* 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(); + public List getTriggersForJob(JobKey jobKey) { + 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()); + synchronized (lock) { + for (TriggerWrapper tw : triggers) { + if (tw.jobKey.equals(jobKey)) { + trigList.add((OperableTrigger) tw.trigger.clone()); + } } } - return (Trigger[]) trigList.toArray(new Trigger[trigList.size()]); + return trigList; } - protected ArrayList getTriggerWrappersForJob(String jobName, String groupName) { - ArrayList trigList = new ArrayList(); + protected ArrayList getTriggerWrappersForJob(JobKey jobKey) { + 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); + synchronized (lock) { + for (TriggerWrapper trigger : triggers) { + if (trigger.jobKey.equals(jobKey)) { + trigList.add(trigger); + } } } return trigList; } - protected ArrayList getTriggerWrappersForCalendar(String calName) { - ArrayList trigList = new ArrayList(); + 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); + synchronized (lock) { + for (TriggerWrapper tw : triggers) { String tcalName = tw.getTrigger().getCalendarName(); - if (tcalName != null && tcalName.equals(calName)) + if (tcalName != null && tcalName.equals(calName)) { trigList.add(tw); + } } } return trigList; } - + /** *

* Pause the {@link Trigger} with the given name. *

- * + * */ - public void pauseTrigger(SchedulingContext ctxt, String triggerName, - String groupName) { + public void pauseTrigger(TriggerKey triggerKey) { - TriggerWrapper tw = (TriggerWrapper) triggersByFQN.get(TriggerWrapper - .getTriggerNameKey(triggerName, groupName)); + synchronized (lock) { + TriggerWrapper tw = triggersByKey.get(triggerKey); + + // does the trigger exist? + if (tw == null || tw.trigger == null) { + return; + } + + // if the trigger is "complete" pausing it does not make sense... + if (tw.state == TriggerWrapper.STATE_COMPLETE) { + return; + } - // does the trigger exist? - if (tw == null || tw.trigger == null) return; - // if the trigger is "complete" pausing it does not make sense... - if (tw.state == TriggerWrapper.STATE_COMPLETE) return; - - synchronized (triggerLock) { - if(tw.state == TriggerWrapper.STATE_BLOCKED) + if(tw.state == TriggerWrapper.STATE_BLOCKED) { tw.state = TriggerWrapper.STATE_PAUSED_BLOCKED; - else + } else { tw.state = TriggerWrapper.STATE_PAUSED; + } + timeTriggers.remove(tw); } } /** *

- * Pause all of the {@link Trigger}s in the given group. + * Pause all of the known {@link Trigger}s matching. *

- * + * *

- * 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 + * The JobStore should "remember" the groups paused, and impose the + * pause on any new triggers that are added to one of these groups while the group is * paused. *

- * + * */ - public void pauseTriggerGroup(SchedulingContext ctxt, String groupName) { + public List pauseTriggers(GroupMatcher matcher) { - synchronized (pausedTriggerGroups) { - if (pausedTriggerGroups.contains(groupName)) return; - pausedTriggerGroups.add(groupName); - String[] names = getTriggerNames(ctxt, groupName); + List pausedGroups; + synchronized (lock) { + pausedGroups = new LinkedList(); - for (int i = 0; i < names.length; i++) { - pauseTrigger(ctxt, names[i], groupName); + StringMatcher.StringOperatorName operator = matcher.getCompareWithOperator(); + switch (operator) { + case EQUALS: + if(pausedTriggerGroups.add(matcher.getCompareToValue())) { + pausedGroups.add(matcher.getCompareToValue()); + } + break; + default : + for (String group : triggersByGroup.keySet()) { + if(operator.evaluate(group, matcher.getCompareToValue())) { + if(pausedTriggerGroups.add(matcher.getCompareToValue())) { + pausedGroups.add(group); + } + } + } } + + for (String pausedGroup : pausedGroups) { + Set keys = getTriggerKeys(GroupMatcher.triggerGroupEquals(pausedGroup)); + + for (TriggerKey key: keys) { + pauseTrigger(key); + } + } } + + return pausedGroups; } /** *

* Pause the {@link org.quartz.JobDetail} with the given * name - by pausing all of its current Triggers. *

- * + * */ - public void pauseJob(SchedulingContext ctxt, String jobName, - String groupName) { - synchronized (pausedTriggerGroups) { - Trigger[] triggers = getTriggersForJob(ctxt, jobName, groupName); - for (int j = 0; j < triggers.length; j++) { - pauseTrigger(ctxt, triggers[j].getName(), triggers[j].getGroup()); + public void pauseJob(JobKey jobKey) { + synchronized (lock) { + List triggersOfJob = getTriggersForJob(jobKey); + for (OperableTrigger trigger: triggersOfJob) { + pauseTrigger(trigger.getKey()); } } } @@ -894,65 +1103,89 @@ * Pause all of the {@link org.quartz.JobDetail}s in the * given group - by pausing all of their Triggers. *

- * - * + * + * *

* 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 (pausedTriggerGroups) { - String[] jobNames = getJobNames(ctxt, groupName); + public List pauseJobs(GroupMatcher matcher) { + List pausedGroups = new LinkedList(); + synchronized (lock) { - 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()); + StringMatcher.StringOperatorName operator = matcher.getCompareWithOperator(); + switch (operator) { + case EQUALS: + if (pausedJobGroups.add(matcher.getCompareToValue())) { + pausedGroups.add(matcher.getCompareToValue()); + } + break; + default : + for (String group : jobsByGroup.keySet()) { + if(operator.evaluate(group, matcher.getCompareToValue())) { + if (pausedJobGroups.add(group)) { + pausedGroups.add(group); + } + } + } + } + + for (String groupName : pausedGroups) { + for (JobKey jobKey: getJobKeys(GroupMatcher.jobGroupEquals(groupName))) { + List triggersOfJob = getTriggersForJob(jobKey); + for (OperableTrigger trigger: triggersOfJob) { + pauseTrigger(trigger.getKey()); + } } } } + + return pausedGroups; } /** *

* Resume (un-pause) the {@link Trigger} with the given - * name. + * key. *

- * + * *

* If the Trigger missed one or more fire-times, then the * Trigger's misfire instruction will be applied. *

- * + * */ - public void resumeTrigger(SchedulingContext ctxt, String triggerName, - String groupName) { + public void resumeTrigger(TriggerKey triggerKey) { - TriggerWrapper tw = (TriggerWrapper) triggersByFQN.get(TriggerWrapper - .getTriggerNameKey(triggerName, groupName)); + synchronized (lock) { + TriggerWrapper tw = triggersByKey.get(triggerKey); + + // does the trigger exist? + if (tw == null || tw.trigger == null) { + return; + } + + OperableTrigger trig = tw.getTrigger(); + + // if the trigger is not paused resuming it does not make sense... + if (tw.state != TriggerWrapper.STATE_PAUSED && + tw.state != TriggerWrapper.STATE_PAUSED_BLOCKED) { + return; + } - Trigger trig = tw.getTrigger(); - - // does the trigger exist? - if (tw == null || tw.trigger == null) return; - // if the trigger is not paused resuming it does not make sense... - if (tw.state != TriggerWrapper.STATE_PAUSED && - tw.state != TriggerWrapper.STATE_PAUSED_BLOCKED) - return; - - synchronized (triggerLock) { - if(blockedJobs.contains( JobWrapper.getJobNameKey(trig.getJobName(), trig.getJobGroup()) )) + if(blockedJobs.contains( trig.getJobKey() )) { tw.state = TriggerWrapper.STATE_BLOCKED; - else + } else { tw.state = TriggerWrapper.STATE_WAITING; - + } + applyMisfire(tw); - - if (tw.state == TriggerWrapper.STATE_WAITING) timeTriggers.add(tw); + + if (tw.state == TriggerWrapper.STATE_WAITING) { + timeTriggers.add(tw); + } } } @@ -961,45 +1194,56 @@ * 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. *

- * + * */ - public void resumeTriggerGroup(SchedulingContext ctxt, String groupName) { + public List resumeTriggers(GroupMatcher matcher) { + Set groups = new HashSet(); - synchronized (pausedTriggerGroups) { - String[] names = getTriggerNames(ctxt, groupName); + synchronized (lock) { + Set keys = getTriggerKeys(matcher); - for (int i = 0; i < names.length; i++) { - resumeTrigger(ctxt, names[i], groupName); + for (TriggerKey triggerKey: keys) { + groups.add(triggerKey.getGroup()); + if(triggersByKey.get(triggerKey) != null) { + String jobGroup = triggersByKey.get(triggerKey).jobKey.getGroup(); + if(pausedJobGroups.contains(jobGroup)) { + continue; + } + } + resumeTrigger(triggerKey); } - pausedTriggerGroups.remove(groupName); + for (String group : groups) { + pausedTriggerGroups.remove(group); + } } + + return new ArrayList(groups); } /** *

* Resume (un-pause) the {@link 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. *

- * + * */ - public void resumeJob(SchedulingContext ctxt, String jobName, - String groupName) { + public void resumeJob(JobKey jobKey) { - synchronized (pausedTriggerGroups) { - Trigger[] triggers = getTriggersForJob(ctxt, jobName, groupName); - for (int j = 0; j < triggers.length; j++) { - resumeTrigger(ctxt, triggers[j].getName(), triggers[j].getGroup()); + synchronized (lock) { + List triggersOfJob = getTriggersForJob(jobKey); + for (OperableTrigger trigger: triggersOfJob) { + resumeTrigger(trigger.getKey()); } } } @@ -1009,50 +1253,61 @@ * Resume (un-pause) all of the {@link 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. *

- * + * */ - public void resumeJobGroup(SchedulingContext ctxt, String groupName) { - synchronized (pausedTriggerGroups) { - String[] jobNames = getJobNames(ctxt, groupName); + public Collection resumeJobs(GroupMatcher matcher) { + Set resumedGroups = new HashSet(); + synchronized (lock) { + Set keys = getJobKeys(matcher); - for (int i = 0; i < jobNames.length; i++) { - Trigger[] triggers = getTriggersForJob(ctxt, jobNames[i], - groupName); - for (int j = 0; j < triggers.length; j++) { - resumeTrigger(ctxt, triggers[j].getName(), - triggers[j].getGroup()); + for (String pausedJobGroup : pausedJobGroups) { + if(matcher.getCompareWithOperator().evaluate(pausedJobGroup, matcher.getCompareToValue())) { + resumedGroups.add(pausedJobGroup); } } + + for (String resumedGroup : resumedGroups) { + pausedJobGroups.remove(resumedGroup); + } + + for (JobKey key: keys) { + List triggersOfJob = getTriggersForJob(key); + for (OperableTrigger trigger: triggersOfJob) { + resumeTrigger(trigger.getKey()); + } + } } + return resumedGroups; } /** *

* Pause all triggers - equivalent of calling pauseTriggerGroup(group) * on every group. *

- * + * *

* When resumeAll() is called (to un-pause), trigger misfire * instructions WILL be applied. *

- * - * @see #resumeAll(SchedulingContext) - * @see #pauseTriggerGroup(SchedulingContext, String) + * + * @see #resumeAll() + * @see #pauseTrigger(org.quartz.TriggerKey) + * @see #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) */ - public void pauseAll(SchedulingContext ctxt) { + public void pauseAll() { - synchronized (pausedTriggerGroups) { - String[] names = getTriggerGroupNames(ctxt); + synchronized (lock) { + List names = getTriggerGroupNames(); - for (int i = 0; i < names.length; i++) { - pauseTriggerGroup(ctxt, names[i]); + for (String name: names) { + pauseTriggers(GroupMatcher.triggerGroupEquals(name)); } } } @@ -1062,108 +1317,139 @@ * 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. *

- * - * @see #pauseAll(SchedulingContext) + * + * @see #pauseAll() */ - public void resumeAll(SchedulingContext ctxt) { + public void resumeAll() { - synchronized (pausedTriggerGroups) { - String[] names = getTriggerGroupNames(ctxt); - - for (int i = 0; i < names.length; i++) { - resumeTriggerGroup(ctxt, names[i]); - } + synchronized (lock) { + pausedJobGroups.clear(); + resumeTriggers(GroupMatcher.anyTriggerGroup()); } } protected boolean applyMisfire(TriggerWrapper tw) { long misfireTime = System.currentTimeMillis(); - if (getMisfireThreshold() > 0) misfireTime -= getMisfireThreshold(); + if (getMisfireThreshold() > 0) { + misfireTime -= getMisfireThreshold(); + } - java.util.Date tnft = tw.trigger.getNextFireTime(); - if (tnft.getTime() > misfireTime) { return false; } + Date tnft = tw.trigger.getNextFireTime(); + if (tnft == null || tnft.getTime() > misfireTime + || tw.trigger.getMisfireInstruction() == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) { + return false; + } Calendar cal = null; - if (tw.trigger.getCalendarName() != null) - cal = retrieveCalendar(null, tw.trigger.getCalendarName()); + if (tw.trigger.getCalendarName() != null) { + cal = retrieveCalendar(tw.trigger.getCalendarName()); + } - signaler.notifyTriggerListenersMisfired(tw.trigger); + signaler.notifyTriggerListenersMisfired((OperableTrigger)tw.trigger.clone()); tw.trigger.updateAfterMisfire(cal); if (tw.trigger.getNextFireTime() == null) { tw.state = TriggerWrapper.STATE_COMPLETE; - synchronized (triggerLock) { + signaler.notifySchedulerListenersFinalized(tw.trigger); + synchronized (lock) { timeTriggers.remove(tw); } - } else if (tnft.equals(tw.trigger.getNextFireTime())) return false; + } else if (tnft.equals(tw.trigger.getNextFireTime())) { + return false; + } return true; } - private static long ftrCtr = System.currentTimeMillis(); + private static final AtomicLong ftrCtr = new AtomicLong(System.currentTimeMillis()); - protected synchronized String getFiredTriggerRecordId() { - return String.valueOf(ftrCtr++); + protected String getFiredTriggerRecordId() { + return String.valueOf(ftrCtr.incrementAndGet()); } /** *

* Get a handle to the next trigger to be fired, and mark it as 'reserved' * by the calling scheduler. *

- * - * @see #releaseAcquiredTrigger(SchedulingContext, Trigger) + * + * @see #releaseAcquiredTrigger(OperableTrigger) */ - public Trigger acquireNextTrigger(SchedulingContext ctxt, long noLaterThan) { - TriggerWrapper tw = null; + public List acquireNextTriggers(long noLaterThan, int maxCount, long timeWindow) { + synchronized (lock) { + List result = new ArrayList(); + Set acquiredJobKeysForNoConcurrentExec = new HashSet(); + Set excludedTriggers = new HashSet(); + long firstAcquiredTriggerFireTime = 0; + + // return empty list if store has no triggers. + if (timeTriggers.size() == 0) + return result; + + while (true) { + TriggerWrapper tw; - synchronized (triggerLock) { - - while (tw == null) { try { - tw = (TriggerWrapper) timeTriggers.first(); + tw = timeTriggers.first(); + if (tw == null) + break; + timeTriggers.remove(tw); } catch (java.util.NoSuchElementException nsee) { - return null; + break; } - 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; + if (tw.trigger.getNextFireTime() != null) { + timeTriggers.add(tw); + } continue; } - if(tw.trigger.getNextFireTime().getTime() > noLaterThan) { + if (tw.getTrigger().getNextFireTime().getTime() > noLaterThan + timeWindow) { timeTriggers.add(tw); - return null; + break; } - tw.state = TriggerWrapper.STATE_ACQUIRED; + // If trigger's job is set as @DisallowConcurrentExecution, and it has already been added to result, then + // put it back into the timeTriggers set and continue to search for next trigger. + JobKey jobKey = tw.trigger.getJobKey(); + JobDetail job = jobsByKey.get(tw.trigger.getJobKey()).jobDetail; + if (job.isConcurrentExectionDisallowed()) { + if (acquiredJobKeysForNoConcurrentExec.contains(jobKey)) { + excludedTriggers.add(tw); + continue; // go to next trigger in store. + } else { + acquiredJobKeysForNoConcurrentExec.add(jobKey); + } + } + tw.state = TriggerWrapper.STATE_ACQUIRED; tw.trigger.setFireInstanceId(getFiredTriggerRecordId()); - Trigger trig = (Trigger) tw.trigger.clone(); - return trig; + OperableTrigger trig = (OperableTrigger) tw.trigger.clone(); + result.add(trig); + if(firstAcquiredTriggerFireTime == 0) + firstAcquiredTriggerFireTime = tw.trigger.getNextFireTime().getTime(); + + if (result.size() == maxCount) + break; } + + // If we did excluded triggers to prevent ACQUIRE state due to DisallowConcurrentExecution, we need to add them back to store. + if (excludedTriggers.size() > 0) + timeTriggers.addAll(excludedTriggers); + return result; } - - return null; } /** @@ -1173,12 +1459,11 @@ * (reserved). *

*/ - public void releaseAcquiredTrigger(SchedulingContext ctxt, Trigger trigger) { - synchronized (triggerLock) { - TriggerWrapper tw = (TriggerWrapper) triggersByFQN.get(TriggerWrapper - .getTriggerNameKey(trigger)); - if (tw != null && tw.state == TriggerWrapper.STATE_ACQUIRED) { - tw.state = TriggerWrapper.STATE_WAITING; + public void releaseAcquiredTrigger(OperableTrigger trigger) { + synchronized (lock) { + TriggerWrapper tw = triggersByKey.get(trigger.getKey()); + if (tw != null && tw.state == TriggerWrapper.STATE_ACQUIRED) { + tw.state = TriggerWrapper.STATE_WAITING; timeTriggers.add(tw); } } @@ -1191,62 +1476,67 @@ * that it had previously acquired (reserved). *

*/ - public TriggerFiredBundle triggerFired(SchedulingContext ctxt, - Trigger trigger) { + public List triggersFired(List firedTriggers) { - synchronized (triggerLock) { - TriggerWrapper tw = (TriggerWrapper) triggersByFQN.get(TriggerWrapper - .getTriggerNameKey(trigger)); - // was the trigger deleted since being acquired? - if (tw == null || tw.trigger == null) return null; - // was the trigger completed since being acquired? - if (tw.state == TriggerWrapper.STATE_COMPLETE) return null; - // was the trigger paused since being acquired? - if (tw.state == TriggerWrapper.STATE_PAUSED) return null; - // was the trigger blocked since being acquired? - if (tw.state == TriggerWrapper.STATE_BLOCKED) return null; - // was the trigger paused and blocked since being acquired? - if (tw.state == TriggerWrapper.STATE_PAUSED_BLOCKED) return null; - - Calendar cal = null; - if (tw.trigger.getCalendarName() != null) - cal = retrieveCalendar(ctxt, tw.trigger.getCalendarName()); - Date prevFireTime = trigger.getPreviousFireTime(); - // call triggered on our copy, and the scheduler's copy - tw.trigger.triggered(cal); - trigger.triggered(cal); - //tw.state = TriggerWrapper.STATE_EXECUTING; - tw.state = TriggerWrapper.STATE_WAITING; + synchronized (lock) { + List results = new ArrayList(); - TriggerFiredBundle bndle = new TriggerFiredBundle(retrieveJob(ctxt, - trigger.getJobName(), trigger.getJobGroup()), trigger, cal, - false, new Date(), trigger.getPreviousFireTime(), prevFireTime, - trigger.getNextFireTime()); + for (OperableTrigger trigger : firedTriggers) { + TriggerWrapper tw = triggersByKey.get(trigger.getKey()); + // was the trigger deleted since being acquired? + if (tw == null || tw.trigger == null) { + continue; + } + // was the trigger completed, paused, blocked, etc. since being acquired? + if (tw.state != TriggerWrapper.STATE_ACQUIRED) { + continue; + } - JobDetail job = bndle.getJobDetail(); + Calendar cal = null; + if (tw.trigger.getCalendarName() != null) { + cal = retrieveCalendar(tw.trigger.getCalendarName()); + if(cal == null) + continue; + } + Date prevFireTime = trigger.getPreviousFireTime(); + // in case trigger was replaced between acquiring and firing + timeTriggers.remove(tw); + // call triggered on our copy, and the scheduler's copy + tw.trigger.triggered(cal); + trigger.triggered(cal); + //tw.state = TriggerWrapper.STATE_EXECUTING; + tw.state = TriggerWrapper.STATE_WAITING; - if (job.isStateful()) { - ArrayList trigs = getTriggerWrappersForJob(job.getName(), job - .getGroup()); - Iterator itr = trigs.iterator(); - while (itr.hasNext()) { - TriggerWrapper ttw = (TriggerWrapper) itr.next(); - if(ttw.state == TriggerWrapper.STATE_WAITING) - ttw.state = TriggerWrapper.STATE_BLOCKED; - if(ttw.state == TriggerWrapper.STATE_PAUSED) - ttw.state = TriggerWrapper.STATE_PAUSED_BLOCKED; - timeTriggers.remove(ttw); + TriggerFiredBundle bndle = new TriggerFiredBundle(retrieveJob( + tw.jobKey), trigger, cal, + false, new Date(), trigger.getPreviousFireTime(), prevFireTime, + trigger.getNextFireTime()); + + JobDetail job = bndle.getJobDetail(); + + if (job.isConcurrentExectionDisallowed()) { + ArrayList trigs = getTriggerWrappersForJob(job.getKey()); + for (TriggerWrapper ttw : trigs) { + if (ttw.state == TriggerWrapper.STATE_WAITING) { + ttw.state = TriggerWrapper.STATE_BLOCKED; + } + if (ttw.state == TriggerWrapper.STATE_PAUSED) { + ttw.state = TriggerWrapper.STATE_PAUSED_BLOCKED; + } + timeTriggers.remove(ttw); + } + blockedJobs.add(job.getKey()); + } else if (tw.trigger.getNextFireTime() != null) { + synchronized (lock) { + timeTriggers.add(tw); + } } - blockedJobs.add(JobWrapper.getJobNameKey(job)); - } else if (tw.trigger.getNextFireTime() != null) { - synchronized (triggerLock) { - timeTriggers.add(tw); + + results.add(new TriggerFiredResult(bndle)); } + return results; } - - return bndle; } - } /** *

@@ -1257,118 +1547,108 @@ * is stateful. *

*/ - public void triggeredJobComplete(SchedulingContext ctxt, Trigger trigger, - JobDetail jobDetail, int triggerInstCode) { + public void triggeredJobComplete(OperableTrigger trigger, + JobDetail jobDetail, CompletedExecutionInstruction triggerInstCode) { - synchronized (triggerLock) { + synchronized (lock) { - String jobKey = JobWrapper.getJobNameKey(jobDetail.getName(), jobDetail - .getGroup()); - JobWrapper jw = (JobWrapper) jobsByFQN.get(jobKey); - TriggerWrapper tw = (TriggerWrapper) triggersByFQN.get(TriggerWrapper - .getTriggerNameKey(trigger)); - + JobWrapper jw = jobsByKey.get(jobDetail.getKey()); + TriggerWrapper tw = triggersByKey.get(trigger.getKey()); + // It's possible that the job is null if: // 1- it was deleted during execution // 2- RAMJobStore is being used only for volatile jobs / triggers // from the JDBC job store if (jw != null) { JobDetail jd = jw.jobDetail; - - if (jobDetail.isStateful()) { + + if (jd.isPersistJobDataAfterExecution()) { JobDataMap newData = jobDetail.getJobDataMap(); - if (newData != null) newData.clearDirtyFlag(); - jd.setJobDataMap(newData); - blockedJobs.remove(JobWrapper.getJobNameKey(jd)); - ArrayList trigs = getTriggerWrappersForJob(jd.getName(), jd - .getGroup()); - Iterator itr = trigs.iterator(); - while (itr.hasNext()) { - TriggerWrapper ttw = (TriggerWrapper) itr.next(); - if (ttw.state == TriggerWrapper.STATE_BLOCKED) { - ttw.state = TriggerWrapper.STATE_WAITING; - timeTriggers.add(ttw); - } - if (ttw.state == TriggerWrapper.STATE_PAUSED_BLOCKED) { - ttw.state = TriggerWrapper.STATE_PAUSED; - } + if (newData != null) { + newData = (JobDataMap)newData.clone(); + newData.clearDirtyFlag(); + } + jd = jd.getJobBuilder().setJobData(newData).build(); + jw.jobDetail = jd; + } + if (jd.isConcurrentExectionDisallowed()) { + blockedJobs.remove(jd.getKey()); + ArrayList trigs = getTriggerWrappersForJob(jd.getKey()); + for(TriggerWrapper ttw : trigs) { + if (ttw.state == TriggerWrapper.STATE_BLOCKED) { + ttw.state = TriggerWrapper.STATE_WAITING; + timeTriggers.add(ttw); } + if (ttw.state == TriggerWrapper.STATE_PAUSED_BLOCKED) { + ttw.state = TriggerWrapper.STATE_PAUSED; + } } + signaler.signalSchedulingChange(0L); } - else { // even if it was deleted, there may be cleanup to do - blockedJobs.remove(JobWrapper.getJobNameKey(jobDetail)); + } else { // even if it was deleted, there may be cleanup to do + blockedJobs.remove(jobDetail.getKey()); } // check for trigger deleted during execution... if (tw != null) { - if (triggerInstCode == Trigger.INSTRUCTION_DELETE_TRIGGER) { + if (triggerInstCode == CompletedExecutionInstruction.DELETE_TRIGGER) { if(trigger.getNextFireTime() == null) { // double check for possible reschedule within job // execution, which would cancel the need to delete... - if(tw.getTrigger().getNextFireTime() == null) - removeTrigger(ctxt, trigger.getName(), trigger.getGroup()); + if(tw.getTrigger().getNextFireTime() == null) { + removeTrigger(trigger.getKey()); + } + } else { + removeTrigger(trigger.getKey()); + signaler.signalSchedulingChange(0L); } - else - removeTrigger(ctxt, trigger.getName(), trigger.getGroup()); - } - else if (triggerInstCode == Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE) { + } else if (triggerInstCode == CompletedExecutionInstruction.SET_TRIGGER_COMPLETE) { tw.state = TriggerWrapper.STATE_COMPLETE; - timeTriggers.remove(tw); - } - else if(triggerInstCode == Trigger.INSTRUCTION_SET_TRIGGER_ERROR) { - getLog().info("Trigger " + trigger.getFullName() + " set to ERROR state."); + timeTriggers.remove(tw); + signaler.signalSchedulingChange(0L); + } else if(triggerInstCode == CompletedExecutionInstruction.SET_TRIGGER_ERROR) { + getLog().info("Trigger " + trigger.getKey() + " set to ERROR state."); tw.state = TriggerWrapper.STATE_ERROR; - } - else if (triggerInstCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR) { + signaler.signalSchedulingChange(0L); + } else if (triggerInstCode == CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR) { getLog().info("All triggers of Job " - + trigger.getFullJobName() + " set to ERROR state."); - setAllTriggersOfJobToState( - trigger.getJobName(), - trigger.getJobGroup(), - TriggerWrapper.STATE_ERROR); + + trigger.getJobKey() + " set to ERROR state."); + setAllTriggersOfJobToState(trigger.getJobKey(), TriggerWrapper.STATE_ERROR); + signaler.signalSchedulingChange(0L); + } else if (triggerInstCode == CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_COMPLETE) { + setAllTriggersOfJobToState(trigger.getJobKey(), TriggerWrapper.STATE_COMPLETE); + signaler.signalSchedulingChange(0L); } - else if (triggerInstCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE) { - setAllTriggersOfJobToState( - trigger.getJobName(), - trigger.getJobGroup(), - TriggerWrapper.STATE_COMPLETE); - } } } } - protected void setAllTriggersOfJobToState(String jobName, String jobGroup, int state) { - ArrayList tws = getTriggerWrappersForJob(jobName, jobGroup); - Iterator itr = tws.iterator(); - while (itr.hasNext()) { - TriggerWrapper tw = (TriggerWrapper) itr.next(); + protected void setAllTriggersOfJobToState(JobKey jobKey, int state) { + ArrayList tws = getTriggerWrappersForJob(jobKey); + for (TriggerWrapper tw : tws) { tw.state = state; - if(state != TriggerWrapper.STATE_WAITING) + if (state != TriggerWrapper.STATE_WAITING) { timeTriggers.remove(tw); + } } } + @SuppressWarnings("UnusedDeclaration") protected String peekTriggers() { - StringBuffer str = new StringBuffer(); - TriggerWrapper tw = null; - - synchronized (triggerLock) { - Iterator itr = triggersByFQN.keySet().iterator(); - while (itr.hasNext()) { - tw = (TriggerWrapper) triggersByFQN.get(itr.next()); - str.append(tw.trigger.getName()); + StringBuilder str = new StringBuilder(); + synchronized (lock) { + for (TriggerWrapper triggerWrapper : triggersByKey.values()) { + str.append(triggerWrapper.trigger.getKey().getName()); str.append("/"); } } str.append(" | "); - synchronized (triggerLock) { - Iterator itr = timeTriggers.iterator(); - while (itr.hasNext()) { - tw = (TriggerWrapper) itr.next(); - str.append(tw.trigger.getName()); + synchronized (lock) { + for (TriggerWrapper timeTrigger : timeTriggers) { + str.append(timeTrigger.trigger.getKey().getName()); str.append("->"); } } @@ -1377,17 +1657,36 @@ } /** - * @see org.quartz.spi.JobStore#getPausedTriggerGroups(org.quartz.core.SchedulingContext) + * @see org.quartz.spi.JobStore#getPausedTriggerGroups() */ - public Set getPausedTriggerGroups(SchedulingContext ctxt) throws JobPersistenceException { - HashSet set = new HashSet(); + public Set getPausedTriggerGroups() throws JobPersistenceException { + HashSet set = new HashSet(); set.addAll(pausedTriggerGroups); return set; } + public void setInstanceId(String schedInstId) { + // + } + public void setInstanceName(String schedName) { + // + } + + public void setThreadPoolSize(final int poolSize) { + // + } + + public long getEstimatedTimeToReleaseAndAcquireTrigger() { + return 5; + } + + public boolean isClustered() { + return false; + } + } /******************************************************************************* @@ -1396,131 +1695,110 @@ * Helper Classes. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -class TriggerComparator implements Comparator { +class TriggerWrapperComparator implements Comparator, java.io.Serializable { + + private static final long serialVersionUID = 8809557142191514261L; - public int compare(Object obj1, Object obj2) { - TriggerWrapper trig1 = (TriggerWrapper) obj1; - TriggerWrapper trig2 = (TriggerWrapper) obj2; - - int comp = trig1.trigger.compareTo(trig2.trigger); - - if (comp == 0) - return trig1.trigger.getFullName().compareTo(trig2.trigger.getFullName()); - - return comp; + TriggerTimeComparator ttc = new TriggerTimeComparator(); + + public int compare(TriggerWrapper trig1, TriggerWrapper trig2) { + return ttc.compare(trig1.trigger, trig2.trigger); } + @Override public boolean equals(Object obj) { - if (obj instanceof TriggerComparator) return true; + return (obj instanceof TriggerWrapperComparator); + } - return false; + @Override + public int hashCode() { + return super.hashCode(); } } class JobWrapper { - public String key; + public JobKey key; public JobDetail jobDetail; JobWrapper(JobDetail jobDetail) { this.jobDetail = jobDetail; - key = getJobNameKey(jobDetail); + key = jobDetail.getKey(); } - JobWrapper(JobDetail jobDetail, String key) { - this.jobDetail = jobDetail; - this.key = key; - } - - static String getJobNameKey(JobDetail jobDetail) { - return jobDetail.getGroup() + "_$x$x$_" + jobDetail.getName(); - } - - static String getJobNameKey(String jobName, String groupName) { - return groupName + "_$x$x$_" + jobName; - } - + @Override public boolean equals(Object obj) { if (obj instanceof JobWrapper) { JobWrapper jw = (JobWrapper) obj; - if (jw.key.equals(this.key)) return true; + if (jw.key.equals(this.key)) { + return true; + } } return false; } + @Override public int hashCode() { return key.hashCode(); } - - } class TriggerWrapper { - public String key; + public final TriggerKey key; - public String jobKey; + public final JobKey jobKey; - public Trigger trigger; + public final OperableTrigger trigger; public int state = STATE_WAITING; - public final static int STATE_WAITING = 0; + public static final int STATE_WAITING = 0; - public final static int STATE_ACQUIRED = 1; + public static final int STATE_ACQUIRED = 1; - public final static int STATE_EXECUTING = 2; + @SuppressWarnings("UnusedDeclaration") + public static final int STATE_EXECUTING = 2; - public final static int STATE_COMPLETE = 3; + public static final int STATE_COMPLETE = 3; - public final static int STATE_PAUSED = 4; + public static final int STATE_PAUSED = 4; - public final static int STATE_BLOCKED = 5; + public static final int STATE_BLOCKED = 5; - public final static int STATE_PAUSED_BLOCKED = 6; + public static final int STATE_PAUSED_BLOCKED = 6; - public final static int STATE_ERROR = 7; + public static final int STATE_ERROR = 7; - TriggerWrapper(Trigger trigger) { + TriggerWrapper(OperableTrigger trigger) { + if(trigger == null) + throw new IllegalArgumentException("Trigger cannot be null!"); this.trigger = trigger; - key = getTriggerNameKey(trigger); - this.jobKey = JobWrapper.getJobNameKey(trigger.getJobName(), trigger - .getJobGroup()); + key = trigger.getKey(); + this.jobKey = trigger.getJobKey(); } - TriggerWrapper(Trigger trigger, String key) { - this.trigger = trigger; - this.key = key; - this.jobKey = JobWrapper.getJobNameKey(trigger.getJobName(), trigger - .getJobGroup()); - } - - static String getTriggerNameKey(Trigger trigger) { - return trigger.getGroup() + "_$x$x$_" + trigger.getName(); - } - - static String getTriggerNameKey(String triggerName, String groupName) { - return groupName + "_$x$x$_" + triggerName; - } - + @Override public boolean equals(Object obj) { if (obj instanceof TriggerWrapper) { TriggerWrapper tw = (TriggerWrapper) obj; - if (tw.key.equals(this.key)) return true; + if (tw.key.equals(this.key)) { + return true; + } } return false; } + @Override public int hashCode() { return key.hashCode(); } - public Trigger getTrigger() { + public OperableTrigger getTrigger() { return this.trigger; } - } Index: 3rdParty_sources/quartz/org/quartz/simpl/SimpleClassLoadHelper.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/SimpleClassLoadHelper.java (.../SimpleClassLoadHelper.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/SimpleClassLoadHelper.java (.../SimpleClassLoadHelper.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.simpl; import org.quartz.spi.ClassLoadHelper; @@ -36,6 +33,7 @@ * @see org.quartz.simpl.LoadingLoaderClassLoadHelper * * @author jhouse + * @author pl47ypus */ public class SimpleClassLoadHelper implements ClassLoadHelper { @@ -49,7 +47,7 @@ /** * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling + * including the opportunity to "steal" the class loader off of the calling * thread, which is the thread that is initializing Quartz. */ public void initialize() { @@ -58,10 +56,16 @@ /** * Return the class with the given name. */ - public Class loadClass(String name) throws ClassNotFoundException { + public Class loadClass(String name) throws ClassNotFoundException { return Class.forName(name); } + @SuppressWarnings("unchecked") + public Class loadClass(String name, Class clazz) + throws ClassNotFoundException { + return (Class) loadClass(name); + } + /** * Finds a resource with a given name. This method returns null if no * resource with this name is found. @@ -82,22 +86,26 @@ return getClassLoader().getResourceAsStream(name); } - private ClassLoader getClassLoader() { + /** + * Enable sharing of the class-loader with 3rd party. + * + * @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 represnting the protected + // Create a method instance representing the protected // getCallerClassLoader method of class ClassLoader Method mthd = ClassLoader.class.getDeclaredMethod( - "getCallerClassLoader", new Class[0]); + "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) { + } catch (Throwable all) { // Use this class' class-loader return this.getClass().getClassLoader(); } Index: 3rdParty_sources/quartz/org/quartz/simpl/SimpleInstanceIdGenerator.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/SimpleInstanceIdGenerator.java (.../SimpleInstanceIdGenerator.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/SimpleInstanceIdGenerator.java (.../SimpleInstanceIdGenerator.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -28,13 +28,11 @@ * @see InstanceIdGenerator * @see HostnameInstanceIdGenerator */ -public class SimpleInstanceIdGenerator implements InstanceIdGenerator -{ +public class SimpleInstanceIdGenerator implements InstanceIdGenerator { public String generateInstanceId() throws SchedulerException { try { return InetAddress.getLocalHost().getHostName() + System.currentTimeMillis(); - } - catch (Exception e) { + } catch (Exception e) { throw new SchedulerException("Couldn't get host name!", e); } } Index: 3rdParty_sources/quartz/org/quartz/simpl/SimpleJobFactory.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/SimpleJobFactory.java (.../SimpleJobFactory.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/SimpleJobFactory.java (.../SimpleJobFactory.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -16,10 +16,11 @@ */ package org.quartz.simpl; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.quartz.Job; import org.quartz.JobDetail; +import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.spi.JobFactory; import org.quartz.spi.TriggerFiredBundle; @@ -35,24 +36,30 @@ */ public class SimpleJobFactory implements JobFactory { - private Log log = LogFactory.getLog(SimpleJobFactory.class); + private final Logger log = LoggerFactory.getLogger(getClass()); - public Job newJob(TriggerFiredBundle bundle) throws SchedulerException { + protected Logger getLog() { + return log; + } + + public Job newJob(TriggerFiredBundle bundle, Scheduler Scheduler) throws SchedulerException { JobDetail jobDetail = bundle.getJobDetail(); - Class jobClass = jobDetail.getJobClass(); + Class jobClass = jobDetail.getJobClass(); try { - if(log.isDebugEnabled()) + if(log.isDebugEnabled()) { log.debug( - "Producing instance of Job '" + jobDetail.getFullName() + + "Producing instance of Job '" + jobDetail.getKey() + "', class=" + jobClass.getName()); + } - return (Job) jobClass.newInstance(); + return jobClass.newInstance(); } catch (Exception e) { SchedulerException se = new SchedulerException( "Problem instantiating class '" + jobDetail.getJobClass().getName() + "'", e); throw se; } } -} \ No newline at end of file + +} Index: 3rdParty_sources/quartz/org/quartz/simpl/SimpleThreadPool.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/SimpleThreadPool.java (.../SimpleThreadPool.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/SimpleThreadPool.java (.../SimpleThreadPool.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,16 +15,18 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.simpl; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.quartz.SchedulerConfigException; import org.quartz.spi.ThreadPool; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + /** *

* This is class is a simple implementation of a thread pool, based on the @@ -59,6 +61,7 @@ private int prio = Thread.NORM_PRIORITY; private boolean isShutdown = false; + private boolean handoffPending = false; private boolean inheritLoader = false; @@ -68,14 +71,18 @@ private ThreadGroup threadGroup; - private Runnable nextRunnable; + private final Object nextRunnableLock = new Object(); - private Object nextRunnableLock = new Object(); + private List workers; + private LinkedList availWorkers = new LinkedList(); + private LinkedList busyWorkers = new LinkedList(); - private WorkerThread[] workers; + private String threadNamePrefix; - private String threadNamePrefix = "SimpleThreadPoolWorker"; + private final Logger log = LoggerFactory.getLogger(getClass()); + private String schedulerInstanceName; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -122,8 +129,8 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - public Log getLog() { - return LogFactory.getLog(SimpleThreadPool.class); + public Logger getLog() { + return log; } public int getPoolSize() { @@ -169,13 +176,13 @@ } public void setThreadNamePrefix(String prfx) { - this.threadNamePrefix = prfx; + this.threadNamePrefix = prfx; } - + public String getThreadNamePrefix() { - return threadNamePrefix; + return threadNamePrefix; } - + /** * @return Returns the * threadsInheritContextClassLoaderOfInitializingThread. @@ -203,7 +210,7 @@ this.inheritGroup = inheritGroup; } - + /** * @return Returns the value of makeThreadsDaemons. */ @@ -218,56 +225,78 @@ public void setMakeThreadsDaemons(boolean makeThreadsDaemons) { this.makeThreadsDaemons = makeThreadsDaemons; } + + public void setInstanceId(String schedInstId) { + } + public void setInstanceName(String schedName) { + schedulerInstanceName = schedName; + } + 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(workers != null && workers.size() > 0) // already initialized... + return; + + 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()) + if(isThreadsInheritGroupOfInitializingThread()) { threadGroup = Thread.currentThread().getThreadGroup(); - else { + } else { // follow the threadGroup tree to the root thread group. threadGroup = Thread.currentThread().getThreadGroup(); ThreadGroup parent = threadGroup; - while ( !parent.getName().equals("main") ) - { + while ( !parent.getName().equals("main") ) { threadGroup = parent; parent = threadGroup.getParent(); } - threadGroup = new ThreadGroup(parent, "SimpleThreadPool"); + threadGroup = new ThreadGroup(parent, schedulerInstanceName + "-SimpleThreadPool"); + if (isMakeThreadsDaemons()) { + threadGroup.setDaemon(true); + } } - - if (isThreadsInheritContextClassLoaderOfInitializingThread()) - getLog().info( - "Job execution threads will use class loader of thread: " - + Thread.currentThread().getName()); + if (isThreadsInheritContextClassLoaderOfInitializingThread()) { + getLog().info( + "Job execution threads will use class loader of thread: " + + Thread.currentThread().getName()); + } + // create the worker threads and start them - workers = createWorkerThreads(count); - for (int i = 0; i < count; ++i) { - if (isThreadsInheritContextClassLoaderOfInitializingThread()) { - workers[i].setContextClassLoader(Thread.currentThread() - .getContextClassLoader()); - } + Iterator workerThreads = createWorkerThreads(count).iterator(); + while(workerThreads.hasNext()) { + WorkerThread wt = workerThreads.next(); + wt.start(); + availWorkers.add(wt); } } - protected WorkerThread[] createWorkerThreads(int count) - { - workers = new WorkerThread[count]; - for (int i = 0; i < count; ++i) { - workers[i] = new WorkerThread(this, threadGroup, - getThreadNamePrefix() + "-" + i, - getThreadPriority(), + protected List createWorkerThreads(int createCount) { + workers = new LinkedList(); + for (int i = 1; i<= createCount; ++i) { + String threadPrefix = getThreadNamePrefix(); + if (threadPrefix == null) { + threadPrefix = schedulerInstanceName + "_Worker"; + } + WorkerThread wt = new WorkerThread(this, threadGroup, + threadPrefix + "-" + i, + getThreadPriority(), isMakeThreadsDaemons()); + if (isThreadsInheritContextClassLoaderOfInitializingThread()) { + wt.setContextClassLoader(Thread.currentThread() + .getContextClassLoader()); + } + workers.add(wt); } - + return workers; } @@ -294,58 +323,76 @@ *

*/ public void shutdown(boolean waitForJobsToComplete) { - isShutdown = true; - // signal each worker thread to shut down - for (int i = 0; i < workers.length; i++) { - if (workers[i] != null) workers[i].shutdown(); - } - - // Give waiting (wait(1000)) worker threads a chance to shut down. - // Active worker threads will shut down after finishing their - // current job. synchronized (nextRunnableLock) { + getLog().debug("Shutting down threadpool..."); + + isShutdown = true; + + if(workers == null) // case where the pool wasn't even initialize()ed + return; + + // signal each worker thread to shut down + Iterator workerThreads = workers.iterator(); + while(workerThreads.hasNext()) { + WorkerThread wt = 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 until all worker threads are shut down - int alive = workers.length; - while (alive > 0) { - alive = 0; - for (int i = 0; i < workers.length; i++) { - if (workers[i].isAlive()) { + if (waitForJobsToComplete == true) { + + boolean interrupted = false; + try { + // wait for hand-off in runInThread to complete... + while(handoffPending) { try { - //if (logger.isDebugEnabled()) + nextRunnableLock.wait(100); + } catch(InterruptedException _) { + interrupted = true; + } + } + + // Wait until all worker threads are shut down + while (busyWorkers.size() > 0) { + WorkerThread wt = (WorkerThread) busyWorkers.getFirst(); + try { getLog().debug( - "Waiting for thread no. " + i + "Waiting for thread " + wt.getName() + " to shut down"); - // note: with waiting infinite - join(0) - the - // application - // may appear to 'hang'. Waiting for a finite time - // however - // requires an additional loop (alive). - alive++; - workers[i].join(200); - } catch (InterruptedException ex) { + // note: with waiting infinite time the + // application may appear to 'hang'. + nextRunnableLock.wait(2000); + } catch (InterruptedException _) { + interrupted = true; } } + + workerThreads = workers.iterator(); + while(workerThreads.hasNext()) { + WorkerThread wt = (WorkerThread) workerThreads.next(); + try { + wt.join(); + workerThreads.remove(); + } catch (InterruptedException _) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } } - } - //if (logger.isDebugEnabled()) { - int activeCount = threadGroup.activeCount(); - if (activeCount > 0) - getLog() - .info( - "There are still " - + activeCount - + " worker threads active." - + " See javadoc runInThread(Runnable) for a possible explanation"); - - getLog().debug("shutdown complete"); - //} + getLog().debug("No executing jobs remaining, all threads stopped."); + } + getLog().debug("Shutdown of threadpool complete."); } } @@ -361,80 +408,71 @@ * the Runnable to be added. */ public boolean runInThread(Runnable runnable) { - if (runnable == null) return false; - - if (isShutdown) { - try { - getLog() - .info( - "SimpleThreadPool.runInThread(): thread pool has been shutdown. Runnable will not be executed"); - } catch(Exception e) { - // ignore to help with a tomcat glitch - } - + if (runnable == null) { return false; } synchronized (nextRunnableLock) { - // Wait until a worker thread has taken the previous Runnable - // or until the thread pool is asked to shutdown. - while ((nextRunnable != null) && !isShutdown) { + handoffPending = true; + + // Wait until a worker thread is available + while ((availWorkers.size() < 1) && !isShutdown) { try { - nextRunnableLock.wait(1000); + nextRunnableLock.wait(500); } catch (InterruptedException ignore) { } } - // During normal operation, not shutdown, set the nextRunnable - // and notify the worker threads waiting (getNextRunnable()). if (!isShutdown) { - nextRunnable = runnable; - nextRunnableLock.notifyAll(); + 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; } - // If the thread pool is going down, execute the Runnable - // within a new additional worker thread (no thread from the pool). - // note: the synchronized section should be as short (time) as - // possible. Starting a new thread is not a quick action. - if (isShutdown) { - new WorkerThread(this, threadGroup, - "WorkerThread-LastJob", prio, false, runnable); - } - return true; } - /** - *

- * Dequeue the next pending Runnable. - *

- * - *

- * getNextRunnable() should return null if within a specific time no new - * Runnable is available. This gives the worker thread the chance to check - * its shutdown flag. In case the worker thread is asked to shut down it - * will notify on nextRunnableLock, hence interrupt the wait state. That - * is, the time used for waiting need not be short. - *

- */ - private Runnable getNextRunnable() throws InterruptedException { - Runnable toRun = null; + public int blockForAvailableThreads() { + synchronized(nextRunnableLock) { - // Wait for new Runnable (see runInThread()) and notify runInThread() - // in case the next Runnable is already waiting. - synchronized (nextRunnableLock) { - if (nextRunnable == null) nextRunnableLock.wait(1000); + while((availWorkers.size() < 1 || handoffPending) && !isShutdown) { + try { + nextRunnableLock.wait(500); + } catch (InterruptedException ignore) { + } + } - if (nextRunnable != null) { - toRun = nextRunnable; - nextRunnable = null; - nextRunnableLock.notifyAll(); + return availWorkers.size(); + } + } + + protected void makeAvailable(WorkerThread wt) { + synchronized(nextRunnableLock) { + if(!isShutdown) { + availWorkers.add(wt); } + busyWorkers.remove(wt); + nextRunnableLock.notifyAll(); } + } - return toRun; + protected void clearFromBusyWorkersList(WorkerThread wt) { + synchronized(nextRunnableLock) { + busyWorkers.remove(wt); + nextRunnableLock.notifyAll(); + } } /* @@ -452,12 +490,16 @@ */ class WorkerThread extends Thread { + private final Object lock = new Object(); + // A flag that signals the WorkerThread to terminate. - private boolean run = true; + private AtomicBoolean run = new AtomicBoolean(true); private SimpleThreadPool tp; private Runnable runnable = null; + + private boolean runOnce = false; /** *

@@ -467,7 +509,7 @@ *

*/ WorkerThread(SimpleThreadPool tp, ThreadGroup threadGroup, String name, - int prio, boolean isDaemon) { + int prio, boolean isDaemon) { this(tp, threadGroup, name, prio, isDaemon, null); } @@ -479,14 +521,15 @@ *

*/ WorkerThread(SimpleThreadPool tp, ThreadGroup threadGroup, String name, - int prio, boolean isDaemon, Runnable runnable) { + int prio, boolean isDaemon, Runnable runnable) { super(threadGroup, name); this.tp = tp; this.runnable = runnable; + if(runnable != null) + runOnce = true; setPriority(prio); setDaemon(isDaemon); - start(); } /** @@ -495,59 +538,81 @@ *

*/ void shutdown() { - run = false; + run.set(false); + } - // @todo I'm not really sure if we should interrupt the thread. - // Javadoc mentions that it interrupts blocked I/O operations as - // well. Hence the job will most likely fail. I think we should - // shut the work thread gracefully, by letting the job finish - // uninterrupted. See SimpleThreadPool.shutdown() - //interrupt(); + public void run(Runnable newRunnable) { + synchronized(lock) { + if(runnable != null) { + throw new IllegalStateException("Already running a Runnable!"); + } + + runnable = newRunnable; + lock.notifyAll(); + } } /** *

* Loop, executing targets as they are received. *

*/ + @Override public void run() { - boolean runOnce = (runnable != null); - - while (run) { + boolean ran = false; + + while (run.get()) { try { - if (runnable == null) runnable = tp.getNextRunnable(); + synchronized(lock) { + while (runnable == null && run.get()) { + lock.wait(500); + } - if (runnable != null) runnable.run(); + 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) { + getLog().error("Worker thread was interrupt()'ed.", unblock); + } catch(Exception e) { // ignore to help with a tomcat glitch } - } catch (Exception exceptionInRunnable) { + } catch (Throwable exceptionInRunnable) { try { getLog().error("Error while executing the Runnable: ", exceptionInRunnable); - } catch(Exception e) { + } catch(Exception e) { // ignore to help with a tomcat glitch } } finally { - if (runOnce) run = false; + synchronized(lock) { + runnable = null; + } + // repair the thread in case the runnable mucked it up... + if(getPriority() != tp.getThreadPriority()) { + setPriority(tp.getThreadPriority()); + } - runnable = null; + if (runOnce) { + run.set(false); + clearFromBusyWorkersList(this); + } else if(ran) { + ran = false; + makeAvailable(this); + } - // repair the thread in case the runnable mucked it up... - setPriority(tp.getThreadPriority()); } } //if (log.isDebugEnabled()) try { - getLog().debug("WorkerThread is shutting down"); - } catch(Exception e) { + getLog().debug("WorkerThread is shut down."); + } catch(Exception e) { // ignore to help with a tomcat glitch } -} + } } } Index: 3rdParty_sources/quartz/org/quartz/simpl/SimpleTimeBroker.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/SimpleTimeBroker.java (.../SimpleTimeBroker.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/SimpleTimeBroker.java (.../SimpleTimeBroker.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.simpl; import java.util.Date; @@ -46,6 +43,7 @@ * * @author James House */ +@SuppressWarnings("deprecation") public class SimpleTimeBroker implements TimeBroker { /* Index: 3rdParty_sources/quartz/org/quartz/simpl/SystemPropertyInstanceIdGenerator.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/simpl/SystemPropertyInstanceIdGenerator.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/simpl/SystemPropertyInstanceIdGenerator.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,106 @@ +package org.quartz.simpl; + +import org.quartz.SchedulerException; +import org.quartz.spi.InstanceIdGenerator; + +/** + * InstanceIdGenerator that will use a {@link SystemPropertyInstanceIdGenerator#SYSTEM_PROPERTY system property} + * to configure the scheduler. The default system property name to use the value of {@link #SYSTEM_PROPERTY}, but + * can be specified via the "systemPropertyName" property. + * + * You can also set the properties "postpend" and "prepend" to String values that will be added to the beginning + * or end (respectively) of the value found in the system property. + * + * If no value set for the property, a {@link org.quartz.SchedulerException} is thrown + * + * @author Alex Snaps + */ +public class SystemPropertyInstanceIdGenerator implements InstanceIdGenerator { + + /** + * System property to read the instanceId from + */ + public static final String SYSTEM_PROPERTY = "org.quartz.scheduler.instanceId"; + + private String prepend = null; + private String postpend = null; + private String systemPropertyName = SYSTEM_PROPERTY; + + /** + * Returns the cluster wide value for this scheduler instance's id, based on a system property + * @return the value of the system property named by the value of {@link #getSystemPropertyName()} - which defaults + * to {@link #SYSTEM_PROPERTY}. + * @throws SchedulerException Shouldn't a value be found + */ + public String generateInstanceId() throws SchedulerException { + String property = System.getProperty(getSystemPropertyName()); + if(property == null) { + throw new SchedulerException("No value for '" + SYSTEM_PROPERTY + + "' system property found, please configure your environment accordingly!"); + } + if(getPrepend() != null) + property = getPrepend() + property; + if(getPostpend() != null) + property = property + getPostpend(); + + return property; + } + + /** + * A String of text to prepend (add to the beginning) to the instanceId + * found in the system property. + */ + public String getPrepend() { + return prepend; + } + + /** + * A String of text to prepend (add to the beginning) to the instanceId + * found in the system property. + * + * @param prepend the value to prepend, or null if none is desired. + */ + public void setPrepend(String prepend) { + this.prepend = prepend == null ? null : prepend.trim(); + } + + /** + * A String of text to postpend (add to the end) to the instanceId + * found in the system property. + */ + public String getPostpend() { + return postpend; + } + + /** + * A String of text to postpend (add to the end) to the instanceId + * found in the system property. + * + * @param postpend the value to postpend, or null if none is desired. + */ + public void setPostpend(String postpend) { + this.postpend = postpend == null ? null : postpend.trim(); + } + + /** + * The name of the system property from which to obtain the instanceId. + * + * Defaults to {@link #SYSTEM_PROPERTY}. + * + */ + public String getSystemPropertyName() { + return systemPropertyName; + } + + /** + * The name of the system property from which to obtain the instanceId. + * + * Defaults to {@link #SYSTEM_PROPERTY}. + * + * @param systemPropertyName the system property name + */ + public void setSystemPropertyName(String systemPropertyName) { + this.systemPropertyName = systemPropertyName == null ? SYSTEM_PROPERTY : systemPropertyName.trim(); + } + +} Index: 3rdParty_sources/quartz/org/quartz/simpl/ThreadContextClassLoadHelper.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/ThreadContextClassLoadHelper.java (.../ThreadContextClassLoadHelper.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/ThreadContextClassLoadHelper.java (.../ThreadContextClassLoadHelper.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.simpl; import org.quartz.spi.ClassLoadHelper; @@ -36,6 +33,7 @@ * @see org.quartz.simpl.LoadingLoaderClassLoadHelper * * @author jhouse + * @author pl47ypus */ public class ThreadContextClassLoadHelper implements ClassLoadHelper { @@ -49,7 +47,7 @@ /** * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling + * including the opportunity to "steal" the class loader off of the calling * thread, which is the thread that is initializing Quartz. */ public void initialize() { @@ -58,10 +56,16 @@ /** * Return the class with the given name. */ - public Class loadClass(String name) throws ClassNotFoundException { + public Class loadClass(String name) throws ClassNotFoundException { return getClassLoader().loadClass(name); } + @SuppressWarnings("unchecked") + public Class loadClass(String name, Class clazz) + throws ClassNotFoundException { + return (Class) loadClass(name); + } + /** * Finds a resource with a given name. This method returns null if no * resource with this name is found. @@ -82,8 +86,13 @@ return getClassLoader().getResourceAsStream(name); } - - private ClassLoader getClassLoader() { + /** + * Enable sharing of the class-loader with 3rd party. + * + * @return the class-loader user be the helper. + */ + public ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); } + } Index: 3rdParty_sources/quartz/org/quartz/simpl/ZeroSizeThreadPool.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/simpl/ZeroSizeThreadPool.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/simpl/ZeroSizeThreadPool.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,114 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.simpl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.quartz.SchedulerConfigException; +import org.quartz.spi.ThreadPool; + +/** + *

+ * This is class is a simple implementation of a zero size thread pool, based on the + * {@link org.quartz.spi.ThreadPool} interface. + *

+ * + *

+ * The pool has zero Threads 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 Logger log = LoggerFactory.getLogger(getClass()); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + *

+ * Create a new ZeroSizeThreadPool. + *

+ */ + public ZeroSizeThreadPool() { + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public Logger getLog() { + return log; + } + + public int getPoolSize() { + return 0; + } + + public void initialize() throws SchedulerConfigException { + } + + public void shutdown() { + shutdown(true); + } + + public void shutdown(boolean waitForJobsToComplete) { + getLog().debug("shutdown complete"); + } + + public boolean runInThread(Runnable runnable) { + throw new UnsupportedOperationException("This ThreadPool should not be used on Scheduler instances that are start()ed."); + } + + public int blockForAvailableThreads() { + throw new UnsupportedOperationException("This ThreadPool should not be used on Scheduler instances that are start()ed."); + } + + public void setInstanceId(String schedInstId) { + } + + public void setInstanceName(String schedName) { + } + +} Index: 3rdParty_sources/quartz/org/quartz/simpl/package.html =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/simpl/package.html (.../package.html) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/simpl/package.html (.../package.html) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -10,8 +10,8 @@


-See the Quartz project - at Open Symphony for more information. +See the Quartz project + for more information. Index: 3rdParty_sources/quartz/org/quartz/spi/ClassLoadHelper.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/ClassLoadHelper.java (.../ClassLoadHelper.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/ClassLoadHelper.java (.../ClassLoadHelper.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.spi; import java.net.URL; @@ -28,42 +25,57 @@ * and resources within the scheduler... * * @author jhouse + * @author pl47ypus */ public interface ClassLoadHelper { - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - /** * Called to give the ClassLoadHelper a chance to initialize itself, - * including the oportunity to "steal" the class loader off of the calling + * including the opportunity to "steal" the class loader off of the calling * thread, which is the thread that is initializing Quartz. */ - public void initialize(); + void initialize(); /** * Return the class with the given name. + * + * @param name the fqcn of the class to load. + * @return the requested class. + * @throws ClassNotFoundException if the class can be found in the classpath. */ - public Class loadClass(String name) throws ClassNotFoundException; + Class loadClass(String name) throws ClassNotFoundException; /** + * Return the class of the given type with the given name. + * + * @param name the fqcn of the class to load. + * @return the requested class. + * @throws ClassNotFoundException if the class can be found in the classpath. + */ + Class loadClass(String name, Class clazz) throws ClassNotFoundException; + + /** * 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); + URL getResource(String 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); + InputStream getResourceAsStream(String name); + + /** + * Enable sharing of the class-loader with 3rd party (e.g. digester). + * + * @return the class-loader user be the helper. + */ + ClassLoader getClassLoader(); } Index: 3rdParty_sources/quartz/org/quartz/spi/InstanceIdGenerator.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/InstanceIdGenerator.java (.../InstanceIdGenerator.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/InstanceIdGenerator.java (.../InstanceIdGenerator.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -21,7 +21,7 @@ /** *

* An InstanceIdGenerator is responsible for generating the clusterwide unique - * instance id for a Scheduler nodde. + * instance id for a Scheduler node. *

* *

@@ -32,8 +32,7 @@ * * @see org.quartz.simpl.SimpleInstanceIdGenerator */ -public interface InstanceIdGenerator -{ +public interface InstanceIdGenerator { /** * Generate the instance id for a Scheduler * Index: 3rdParty_sources/quartz/org/quartz/spi/JobFactory.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/JobFactory.java (.../JobFactory.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/JobFactory.java (.../JobFactory.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -17,6 +17,7 @@ package org.quartz.spi; import org.quartz.Job; +import org.quartz.Scheduler; import org.quartz.SchedulerException; /** @@ -56,9 +57,10 @@ * @param bundle * The TriggerFiredBundle from which the JobDetail * and other info relating to the trigger firing can be obtained. + * @param scheduler a handle to the scheduler that is about to execute the job. * @throws SchedulerException if there is a problem instantiating the Job. * @return the newly instantiated Job */ - public Job newJob(TriggerFiredBundle bundle) throws SchedulerException; + Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException; } Index: 3rdParty_sources/quartz/org/quartz/spi/JobStore.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/JobStore.java (.../JobStore.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/JobStore.java (.../JobStore.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,94 +1,117 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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.spi; +import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.Set; import org.quartz.Calendar; +import org.quartz.Job; import org.quartz.JobDetail; +import org.quartz.JobKey; import org.quartz.JobPersistenceException; import org.quartz.ObjectAlreadyExistsException; import org.quartz.SchedulerConfigException; import org.quartz.SchedulerException; import org.quartz.Trigger; -import org.quartz.core.SchedulingContext; +import org.quartz.TriggerKey; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.Trigger.TriggerState; +import org.quartz.impl.matchers.GroupMatcher; /** *

* The interface to be implemented by classes that want to provide a {@link org.quartz.Job} * and {@link org.quartz.Trigger} storage mechanism for the * {@link 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. *

- * + * * @see org.quartz.core.QuartzScheduler * @see org.quartz.Trigger * @see org.quartz.Job * @see org.quartz.JobDetail * @see org.quartz.JobDataMap * @see org.quartz.Calendar - * + * * @author James House + * @author Eric Mueller */ public interface JobStore { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Interface. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** - *

* Called by the QuartzScheduler before the JobStore is * used, in order to give the it a chance to initialize. - *

*/ - public void initialize(ClassLoadHelper loadHelper, - SchedulerSignaler signaler) throws SchedulerConfigException; + void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler) + throws SchedulerConfigException; /** - *

* Called by the QuartzScheduler to inform the JobStore that * the scheduler has started. - *

*/ - public void schedulerStarted() throws SchedulerException ; - + void schedulerStarted() throws SchedulerException ; + /** - *

* Called by the QuartzScheduler to inform the JobStore that + * the scheduler has been paused. + */ + void schedulerPaused(); + + /** + * Called by the QuartzScheduler to inform the JobStore that + * the scheduler has resumed after being paused. + */ + void schedulerResumed(); + + /** + * Called by the QuartzScheduler to inform the JobStore that * it should free up all of it's resources because the scheduler is * shutting down. - *

*/ - public void shutdown(); + void shutdown(); - public boolean supportsPersistence(); + boolean supportsPersistence(); + + /** + * How long (in milliseconds) the JobStore implementation + * estimates that it will take to release a trigger and acquire a new one. + */ + long getEstimatedTimeToReleaseAndAcquireTrigger(); + + /** + * Whether or not the JobStore implementation is clustered. + */ + boolean isClustered(); ///////////////////////////////////////////////////////////////////////////// // @@ -97,10 +120,8 @@ ///////////////////////////////////////////////////////////////////////////// /** - *

* Store the given {@link org.quartz.JobDetail} and {@link org.quartz.Trigger}. - *

- * + * * @param newJob * The JobDetail to be stored. * @param newTrigger @@ -109,15 +130,12 @@ * if a Job with the same name/group already * exists. */ - public void storeJobAndTrigger(SchedulingContext ctxt, JobDetail newJob, - Trigger newTrigger) throws ObjectAlreadyExistsException, - JobPersistenceException; + void storeJobAndTrigger(JobDetail newJob, OperableTrigger newTrigger) + throws ObjectAlreadyExistsException, JobPersistenceException; /** - *

* Store the given {@link org.quartz.JobDetail}. - *

- * + * * @param newJob * The JobDetail to be stored. * @param replaceExisting @@ -128,53 +146,44 @@ * 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, - JobPersistenceException; + void storeJob(JobDetail newJob, boolean replaceExisting) + throws ObjectAlreadyExistsException, JobPersistenceException; + public void storeJobsAndTriggers(Map> triggersAndJobs, boolean replace) + throws ObjectAlreadyExistsException, JobPersistenceException; + /** - *

* Remove (delete) the {@link org.quartz.Job} with the given - * name, and any {@link org.quartz.Trigger} s that reference + * key, and any {@link 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. *

- * - * @param jobName - * The name of the 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) throws JobPersistenceException; - + boolean removeJob(JobKey jobKey) + throws JobPersistenceException; + + public boolean removeJobs(List jobKeys) + throws JobPersistenceException; + /** - *

* Retrieve the {@link org.quartz.JobDetail} for the given * {@link org.quartz.Job}. - *

- * - * @param jobName - * The name of the 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) throws JobPersistenceException; + JobDetail retrieveJob(JobKey jobKey) + throws JobPersistenceException; /** - *

* Store the given {@link org.quartz.Trigger}. - *

- * + * * @param newTrigger * The Trigger to be stored. * @param replaceExisting @@ -184,81 +193,90 @@ * @throws ObjectAlreadyExistsException * if a Trigger with the same name/group already * exists, and replaceExisting is set to false. - * - * @see #pauseTriggerGroup(SchedulingContext, String) + * + * @see #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) */ - public void storeTrigger(SchedulingContext ctxt, Trigger newTrigger, - boolean replaceExisting) throws ObjectAlreadyExistsException, - JobPersistenceException; + void storeTrigger(OperableTrigger newTrigger, boolean replaceExisting) + throws ObjectAlreadyExistsException, JobPersistenceException; /** - *

* Remove (delete) the {@link org.quartz.Trigger} with the - * given name. - *

- * + * given key. + * *

* 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. *

- * - * @param triggerName - * The name of the 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) throws JobPersistenceException; + boolean removeTrigger(TriggerKey triggerKey) throws JobPersistenceException; + public boolean removeTriggers(List triggerKeys) + throws JobPersistenceException; + /** - *

* Remove (delete) the {@link org.quartz.Trigger} with the - * given name, and store the new given one - which must be associated + * given key, and store the new given one - which must be associated * with the same job. - *

* - * @param triggerName - * The name of the 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. */ - public boolean replaceTrigger(SchedulingContext ctxt, String triggerName, - String groupName, Trigger newTrigger) throws JobPersistenceException; + boolean replaceTrigger(TriggerKey triggerKey, OperableTrigger newTrigger) + throws JobPersistenceException; - /** - *

* Retrieve the given {@link org.quartz.Trigger}. - *

- * - * @param triggerName - * The name of the 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) throws JobPersistenceException; + OperableTrigger retrieveTrigger(TriggerKey triggerKey) throws JobPersistenceException; + /** - *

- * Store the given {@link org.quartz.Calendar}. - *

+ * Determine whether a {@link Job} with the given identifier already + * exists within the scheduler. * + * @param jobKey the identifier to check for + * @return true if a Job exists with the given identifier + * @throws SchedulerException + */ + boolean checkExists(JobKey jobKey) throws JobPersistenceException; + + /** + * Determine whether a {@link Trigger} with the given identifier already + * exists within the scheduler. + * + * @param triggerKey the identifier to check for + * @return true if a Trigger exists with the given identifier + * @throws SchedulerException + */ + boolean checkExists(TriggerKey triggerKey) throws JobPersistenceException; + + /** + * Clear (delete!) all scheduling data - all {@link Job}s, {@link Trigger}s + * {@link Calendar}s. + * + * @throws JobPersistenceException + */ + void clearAllSchedulingData() throws JobPersistenceException; + + /** + * Store the given {@link org.quartz.Calendar}. + * * @param calendar * The Calendar to be stored. * @param replaceExisting @@ -267,47 +285,42 @@ * should be over-written. * @param updateTriggers * If true, any Triggers existing - * in the JobStore that reference an 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, JobPersistenceException; + void storeCalendar(String name, Calendar calendar, boolean replaceExisting, boolean updateTriggers) + throws ObjectAlreadyExistsException, JobPersistenceException; /** - *

* Remove (delete) the {@link org.quartz.Calendar} with the * given name. - *

- * + * *

* If removal of the Calendar would result in - * s pointing to non-existent calendars, then a + * Triggers pointing to non-existent calendars, then a * JobPersistenceException will be thrown.

* * * @param calName The name of the 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; + boolean removeCalendar(String calName) + throws JobPersistenceException; /** - *

* Retrieve the given {@link org.quartz.Trigger}. - *

- * + * * @param calName * The name of the Calendar to be retrieved. * @return The desired Calendar, or null if there is no * match. */ - public Calendar retrieveCalendar(SchedulingContext ctxt, String calName) - throws JobPersistenceException; + Calendar retrieveCalendar(String calName) + throws JobPersistenceException; ///////////////////////////////////////////////////////////////////////////// // @@ -316,127 +329,101 @@ ///////////////////////////////////////////////////////////////////////////// /** - *

* Get the number of {@link org.quartz.Job} s that are * stored in the JobsStore. - *

*/ - public int getNumberOfJobs(SchedulingContext ctxt) - throws JobPersistenceException; + int getNumberOfJobs() + throws JobPersistenceException; /** - *

* Get the number of {@link org.quartz.Trigger} s that are * stored in the JobsStore. - *

*/ - public int getNumberOfTriggers(SchedulingContext ctxt) - throws JobPersistenceException; + int getNumberOfTriggers() + throws JobPersistenceException; /** - *

* Get the number of {@link org.quartz.Calendar} s that are * stored in the JobsStore. - *

*/ - public int getNumberOfCalendars(SchedulingContext ctxt) - throws JobPersistenceException; + int getNumberOfCalendars() + throws JobPersistenceException; /** - *

- * Get the names of all of the {@link org.quartz.Job} s that + * Get the keys of all of the {@link 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). + * If there are no jobs in the given group name, the result should be + * an empty collection (not null). *

*/ - public String[] getJobNames(SchedulingContext ctxt, String groupName) - throws JobPersistenceException; + Set getJobKeys(GroupMatcher matcher) + throws JobPersistenceException; /** - *

* Get the names of all of the {@link 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). *

*/ - public String[] getTriggerNames(SchedulingContext ctxt, String groupName) - throws JobPersistenceException; + Set getTriggerKeys(GroupMatcher matcher) + throws JobPersistenceException; /** - *

* Get the names of all of the {@link org.quartz.Job} * groups. - *

- * + * *

* If there are no known group names, the result should be a zero-length * array (not null). *

*/ - public String[] getJobGroupNames(SchedulingContext ctxt) - throws JobPersistenceException; + List getJobGroupNames() + throws JobPersistenceException; /** - *

* Get the names of all of the {@link org.quartz.Trigger} * groups. - *

- * + * *

* If there are no known group names, the result should be a zero-length * array (not null). *

*/ - public String[] getTriggerGroupNames(SchedulingContext ctxt) - throws JobPersistenceException; + List getTriggerGroupNames() + throws JobPersistenceException; /** - *

* Get the names of all of the {@link 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). *

*/ - public String[] getCalendarNames(SchedulingContext ctxt) - throws JobPersistenceException; + List getCalendarNames() + throws JobPersistenceException; /** - *

* 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) throws JobPersistenceException; + List getTriggersForJob(JobKey jobKey) throws JobPersistenceException; /** - *

* Get the current state of the identified {@link Trigger}. - *

- * - * @see Trigger#STATE_NORMAL - * @see Trigger#STATE_PAUSED - * @see Trigger#STATE_COMPLETE - * @see Trigger#STATE_ERROR - * @see Trigger#STATE_NONE + * + * @see Trigger.TriggerState */ - public int getTriggerState(SchedulingContext ctxt, String triggerName, - String triggerGroup) throws JobPersistenceException; + TriggerState getTriggerState(TriggerKey triggerKey) throws JobPersistenceException; ///////////////////////////////////////////////////////////////////////////// // @@ -445,162 +432,136 @@ ///////////////////////////////////////////////////////////////////////////// /** - *

- * Pause the {@link org.quartz.Trigger} with the given name. - *

- * - * @see #resumeTrigger(SchedulingContext, String, String) + * Pause the {@link org.quartz.Trigger} with the given key. + * + * @see #resumeTrigger(TriggerKey) */ - public void pauseTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws JobPersistenceException; + void pauseTrigger(TriggerKey triggerKey) throws JobPersistenceException; /** - *

* Pause all of the {@link 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) + * + * @see #resumeTriggerGroup(String) */ - public void pauseTriggerGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException; + Collection pauseTriggers(GroupMatcher matcher) throws JobPersistenceException; /** - *

* Pause the {@link org.quartz.Job} with the given name - by * pausing all of its current Triggers. - *

- * - * @see #resumeJob(SchedulingContext, String, String) + * + * @see #resumeJob(JobKey) */ - public void pauseJob(SchedulingContext ctxt, String jobName, - String groupName) throws JobPersistenceException; + void pauseJob(JobKey jobKey) throws JobPersistenceException; /** - *

* Pause all of the {@link org.quartz.Job}s in the given * group - by pausing all of their Triggers. - *

- * + * *

* 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) + * + * @see #resumeJobGroup(String) */ - public void pauseJobGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException; + Collection pauseJobs(GroupMatcher groupMatcher) + throws JobPersistenceException; /** - *

* Resume (un-pause) the {@link org.quartz.Trigger} with the - * given name. - *

- * + * given key. + * *

* If the Trigger missed one or more fire-times, then the * Trigger's misfire instruction will be applied. *

- * - * @see #pauseTrigger(SchedulingContext, String, String) + * + * @see #pauseTrigger(TriggerKey) */ - public void resumeTrigger(SchedulingContext ctxt, String triggerName, - String groupName) throws JobPersistenceException; + void resumeTrigger(TriggerKey triggerKey) throws JobPersistenceException; /** - *

* Resume (un-pause) all of the {@link 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. *

- * - * @see #pauseTriggerGroup(SchedulingContext, String) + * + * @see #pauseTriggers(String) */ - public void resumeTriggerGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException; + Collection resumeTriggers(GroupMatcher matcher) + throws JobPersistenceException; - public Set getPausedTriggerGroups(SchedulingContext ctxt) + Set getPausedTriggerGroups() throws JobPersistenceException; - /** - *

* Resume (un-pause) the {@link org.quartz.Job} with the - * given name. - *

- * + * given key. + * *

* If any of the Job'sTrigger s missed one * or more fire-times, then the Trigger's misfire * instruction will be applied. *

- * - * @see #pauseJob(SchedulingContext, String, String) + * + * @see #pauseJob(JobKey) */ - public void resumeJob(SchedulingContext ctxt, String jobName, - String groupName) throws JobPersistenceException; + void resumeJob(JobKey jobKey) throws JobPersistenceException; /** - *

* Resume (un-pause) all of the {@link 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. *

- * - * @see #pauseJobGroup(SchedulingContext, String) + * + * @see #pauseJobGroup(String) */ - public void resumeJobGroup(SchedulingContext ctxt, String groupName) - throws JobPersistenceException; + Collection resumeJobs(GroupMatcher matcher) + throws JobPersistenceException; /** - *

* Pause all triggers - equivalent of calling pauseTriggerGroup(group) * on every group. - *

- * + * *

* When resumeAll() is called (to un-pause), trigger misfire * instructions WILL be applied. *

- * - * @see #resumeAll(SchedulingContext) - * @see #pauseTriggerGroup(SchedulingContext, String) + * + * @see #resumeAll() + * @see #pauseTriggers(String) */ - public void pauseAll(SchedulingContext ctxt) throws JobPersistenceException; + void pauseAll() throws JobPersistenceException; /** - *

* 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. *

- * - * @see #pauseAll(SchedulingContext) + * + * @see #pauseAll() */ - public void resumeAll(SchedulingContext ctxt) - throws JobPersistenceException; + void resumeAll() + throws JobPersistenceException; ///////////////////////////////////////////////////////////////////////////// // @@ -609,54 +570,66 @@ ///////////////////////////////////////////////////////////////////////////// /** - *

* 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 + * @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) + * @see #releaseAcquiredTrigger(Trigger) */ - public Trigger acquireNextTrigger(SchedulingContext ctxt, long noLaterThan) - throws JobPersistenceException; + List acquireNextTriggers(long noLaterThan, int maxCount, long timeWindow) + throws JobPersistenceException; /** - *

* Inform the JobStore that the scheduler no longer plans to * fire the given Trigger, that it had previously acquired * (reserved). - *

*/ - public void releaseAcquiredTrigger(SchedulingContext ctxt, Trigger trigger) - throws JobPersistenceException; + void releaseAcquiredTrigger(OperableTrigger trigger); /** - *

* Inform the JobStore that the scheduler is now firing the * given Trigger (executing its associated Job), * that it had previously acquired (reserved). - *

- * - * @return null if the trigger or it's job or calendar no longer exist, or + * + * @return may return null if all the triggers or their calendars no longer exist, or * if the trigger was not successfully put into the 'executing' - * state. + * state. Preference is to return an empty list if none of the triggers + * could be fired. */ - public TriggerFiredBundle triggerFired(SchedulingContext ctxt, - Trigger trigger) throws JobPersistenceException; + List triggersFired(List triggers) throws JobPersistenceException; /** - *

* Inform the JobStore that the scheduler has completed the - * firing of the given Trigger (and the execution its - * associated Job), and that the {@link org.quartz.JobDataMap} + * firing of the given Trigger (and the execution of its + * associated Job completed, threw an exception, or was vetoed), + * and that the {@link org.quartz.JobDataMap} * in the given JobDetail should be updated if the Job * is stateful. - *

*/ - public void triggeredJobComplete(SchedulingContext ctxt, Trigger trigger, - JobDetail jobDetail, int triggerInstCode) - throws JobPersistenceException; + void triggeredJobComplete(OperableTrigger trigger, JobDetail jobDetail, CompletedExecutionInstruction triggerInstCode); + /** + * Inform the JobStore of the Scheduler instance's Id, + * prior to initialize being invoked. + * + * @since 1.7 + */ + void setInstanceId(String schedInstId); + + /** + * Inform the JobStore of the Scheduler instance's name, + * prior to initialize being invoked. + * + * @since 1.7 + */ + void setInstanceName(String schedName); + + /** + * Tells the JobStore the pool size used to execute jobs + * @param poolSize amount of threads allocated for job execution + * @since 2.0 + */ + void setThreadPoolSize(int poolSize); } Index: 3rdParty_sources/quartz/org/quartz/spi/MutableTrigger.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/spi/MutableTrigger.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/spi/MutableTrigger.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,128 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.spi; + +import java.util.Date; + +import org.quartz.Calendar; +import org.quartz.CronTrigger; +import org.quartz.JobDataMap; +import org.quartz.JobKey; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.quartz.TriggerUtils; + +public interface MutableTrigger extends Trigger { + + public void setKey(TriggerKey key); + + public void setJobKey(JobKey key); + + /** + *

+ * 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. + *

+ */ + public void setDescription(String description); + + /** + *

+ * Associate the {@link Calendar} with the given name with + * this Trigger. + *

+ * + * @param calendarName + * use null to dis-associate a Calendar. + */ + public void setCalendarName(String calendarName); + + /** + *

+ * Set the JobDataMap to be associated with the + * Trigger. + *

+ */ + public void setJobDataMap(JobDataMap jobDataMap); + + /** + * The priority of a Trigger acts as a tie breaker such that if + * two Triggers 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. + *

+ * + * @see #DEFAULT_PRIORITY + */ + public void setPriority(int priority); + + /** + *

+ * 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 void setStartTime(Date startTime); + + /** + *

+ * Set the time at which the Trigger should quit repeating - + * regardless of any remaining repeats (based on the trigger's particular + * repeat settings). + *

+ * + * @see TriggerUtils#computeEndTimeToAllowParticularNumberOfFirings(Trigger, Calendar, int) + */ + public void setEndTime(Date endTime); + + /** + *

+ * 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. + *

+ * + * @see #MISFIRE_INSTRUCTION_SMART_POLICY + * @see #updateAfterMisfire(Calendar) + * @see SimpleTrigger + * @see CronTrigger + */ + public void setMisfireInstruction(int misfireInstruction); + + + public Object clone(); + +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/spi/OperableTrigger.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/spi/OperableTrigger.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/spi/OperableTrigger.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,152 @@ +package org.quartz.spi; + +import java.util.Date; + +import org.quartz.Calendar; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; + +public interface OperableTrigger extends MutableTrigger { + + /** + *

+ * 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). + *

+ * + * @see #executionComplete(JobExecutionContext, JobExecutionException) + */ + public void triggered(Calendar calendar); + + /** + *

+ * 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. + *

+ * + * @return the first time at which the 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); + + /** + *

+ * This method should not be used by the Quartz client. + *

+ * + *

+ * Called after the {@link Scheduler} has executed the + * {@link org.quartz.JobDetail} associated with the Trigger + * in order to get the final instruction code from the trigger. + *

+ * + * @param context + * is the 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 CompletedExecutionInstruction constants. + * + * @see CompletedExecutionInstruction + * @see #triggered(Calendar) + */ + public CompletedExecutionInstruction executionComplete(JobExecutionContext context, JobExecutionException result); + + /** + *

+ * 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. + *

+ */ + public void updateAfterMisfire(Calendar cal); + + /** + *

+ * 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). + *

+ * + * @param cal + */ + public void updateWithNewCalendar(Calendar cal, long misfireThreshold); + + + /** + *

+ * 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; + + + /** + *

+ * This method should not be used by the Quartz client. + *

+ * + *

+ * Usable by {@link org.quartz.spi.JobStore} + * implementations, in order to facilitate 'recognizing' instances of fired + * Trigger s as their jobs complete execution. + *

+ * + * + */ + public void setFireInstanceId(String id); + + /** + *

+ * This method should not be used by the Quartz client. + *

+ */ + public String getFireInstanceId(); + + + public void setNextFireTime(Date nextFireTime); + + public void setPreviousFireTime(Date previousFireTime); +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/spi/SchedulerPlugin.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/SchedulerPlugin.java (.../SchedulerPlugin.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/SchedulerPlugin.java (.../SchedulerPlugin.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.spi; import org.quartz.Scheduler; @@ -41,6 +38,13 @@ * 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. + *

+ * * @author James House */ public interface SchedulerPlugin { @@ -64,16 +68,26 @@ * 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. + *

+ * * @param name * The name by which the plugin is identified. * @param scheduler * The scheduler to which the plugin is registered. + * @param loadHelper + * The classLoadHelper the SchedulerFactory is + * actually using * - * @throws SchedulerConfigException + * @throws org.quartz.SchedulerConfigException * if there is an error initializing. */ - public void initialize(String name, Scheduler scheduler) - throws SchedulerException; + void initialize(String name, Scheduler scheduler, ClassLoadHelper loadHelper) + throws SchedulerException; /** *

@@ -82,7 +96,7 @@ * needs to. *

*/ - public void start(); + void start(); /** *

@@ -91,6 +105,6 @@ * down. *

*/ - public void shutdown(); + void shutdown(); } Index: 3rdParty_sources/quartz/org/quartz/spi/SchedulerSignaler.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/SchedulerSignaler.java (.../SchedulerSignaler.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/SchedulerSignaler.java (.../SchedulerSignaler.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,11 +15,10 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.spi; +import org.quartz.JobKey; +import org.quartz.SchedulerException; import org.quartz.Trigger; /** @@ -38,8 +37,13 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - public void notifyTriggerListenersMisfired(Trigger trigger); + void notifyTriggerListenersMisfired(Trigger trigger); - public void signalSchedulingChange(); + void notifySchedulerListenersFinalized(Trigger trigger); + void notifySchedulerListenersJobDeleted(JobKey jobKey); + + void signalSchedulingChange(long candidateNewNextFireTime); + + void notifySchedulerListenersError(String string, SchedulerException jpe); } Index: 3rdParty_sources/quartz/org/quartz/spi/ThreadExecutor.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/spi/ThreadExecutor.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/spi/ThreadExecutor.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,25 @@ +package org.quartz.spi; + +/** + * Allows different strategies for scheduling threads. The {@link #initialize()} + * method is required to be called before the first call to + * {@link #execute(Thread)}. The Thread containing the work to be performed is + * passed to execute and the work is scheduled by the underlying implementation. + * + * @author matt.accola + * @version $Revision$ $Date$ + */ +public interface ThreadExecutor { + + /** + * Submit a task for execution + * + * @param thread the thread to execute + */ + void execute(Thread thread); + + /** + * Initialize any state prior to calling {@link #execute(Thread)} + */ + void initialize(); +} Index: 3rdParty_sources/quartz/org/quartz/spi/ThreadPool.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/ThreadPool.java (.../ThreadPool.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/ThreadPool.java (.../ThreadPool.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,23 +1,20 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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.spi; import org.quartz.SchedulerConfigException; @@ -27,18 +24,29 @@ * The interface to be implemented by classes that want to provide a thread * pool for the {@link 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. + *

+ * * @see org.quartz.core.QuartzScheduler - * + * * @author James House */ public interface ThreadPool { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Interface. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -47,23 +55,41 @@ * 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 available threads, rather it should either queue the Runnable, or - * block until a thread is available, depending on the desired strategy. + * are no immediately available threads false should be returned. *

+ * + * @return true, if the runnable was assigned to run on a Thread. */ - public boolean runInThread(Runnable runnable); + boolean runInThread(Runnable runnable); /** *

- * Called by the QuartzScheduler before the ThreadPool is + * 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(); + + /** + *

+ * Must be called before the ThreadPool is * used, in order to give the it a chance to initialize. *

+ * + *

Typically called by the SchedulerFactory.

*/ - public void initialize() throws SchedulerConfigException; + void initialize() throws SchedulerConfigException; /** *

@@ -72,7 +98,27 @@ * shutting down. *

*/ - public void shutdown(boolean waitForJobsToComplete); + void shutdown(boolean waitForJobsToComplete); - public int getPoolSize(); + /** + *

Get the current number of threads in the ThreadPool.

+ */ + int getPoolSize(); + + /** + *

Inform the ThreadPool of the Scheduler instance's Id, + * prior to initialize being invoked.

+ * + * @since 1.7 + */ + void setInstanceId(String schedInstId); + + /** + *

Inform the ThreadPool of the Scheduler instance's name, + * prior to initialize being invoked.

+ * + * @since 1.7 + */ + void setInstanceName(String schedName); + } Index: 3rdParty_sources/quartz/org/quartz/spi/TimeBroker.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/TimeBroker.java (.../TimeBroker.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/TimeBroker.java (.../TimeBroker.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.spi; import java.util.Date; @@ -26,6 +23,8 @@ import org.quartz.SchedulerException; /** + *

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 org.quartz.core.QuartzScheduler} can @@ -36,14 +35,14 @@ * In general, the default implementation of this interface ({@link 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 + * 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. *

* * @see org.quartz.core.QuartzScheduler - * + * @deprecated TimeBroker is not currently used in the Quartz code base. * @author James House */ public interface TimeBroker { @@ -65,15 +64,15 @@ * with the error code set to * SchedulerException.ERR_TIME_BROKER_FAILURE */ - public Date getCurrentTime() throws SchedulerException; + Date getCurrentTime() throws SchedulerException; /** *

* Called by the QuartzScheduler before the TimeBroker is * used, in order to give the it a chance to initialize. *

*/ - public void initialize() throws SchedulerConfigException; + void initialize() throws SchedulerConfigException; /** *

@@ -82,6 +81,6 @@ * shutting down. *

*/ - public void shutdown(); + void shutdown(); } Index: 3rdParty_sources/quartz/org/quartz/spi/TriggerFiredBundle.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/TriggerFiredBundle.java (.../TriggerFiredBundle.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/TriggerFiredBundle.java (.../TriggerFiredBundle.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,28 +15,26 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.spi; import java.util.Date; import org.quartz.Calendar; import org.quartz.JobDetail; -import org.quartz.Trigger; /** *

* A simple class (structure) used for returning execution-time data from the * JobStore to the QuartzSchedulerThread. *

* - * @see org.quartz.core.QuartzScheduler + * @see org.quartz.core.QuartzSchedulerThread * * @author James House */ public class TriggerFiredBundle implements java.io.Serializable { + + private static final long serialVersionUID = -6414106108306999265L; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -48,7 +46,7 @@ private JobDetail job; - private Trigger trigger; + private OperableTrigger trigger; private Calendar cal; @@ -70,7 +68,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - public TriggerFiredBundle(JobDetail job, Trigger trigger, Calendar cal, + public TriggerFiredBundle(JobDetail job, OperableTrigger trigger, Calendar cal, boolean jobIsRecovering, Date fireTime, Date scheduledFireTime, Date prevFireTime, Date nextFireTime) { this.job = job; @@ -95,7 +93,7 @@ return job; } - public Trigger getTrigger() { + public OperableTrigger getTrigger() { return trigger; } Index: 3rdParty_sources/quartz/org/quartz/spi/TriggerFiredResult.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/spi/TriggerFiredResult.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/spi/TriggerFiredResult.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,27 @@ +package org.quartz.spi; + +/** + * @author lorban + */ +public class TriggerFiredResult { + + private TriggerFiredBundle triggerFiredBundle; + + private Exception exception; + + public TriggerFiredResult(TriggerFiredBundle triggerFiredBundle) { + this.triggerFiredBundle = triggerFiredBundle; + } + + public TriggerFiredResult(Exception exception) { + this.exception = exception; + } + + public TriggerFiredBundle getTriggerFiredBundle() { + return triggerFiredBundle; + } + + public Exception getException() { + return exception; + } +} Index: 3rdParty_sources/quartz/org/quartz/spi/package.html =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/spi/package.html (.../package.html) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/spi/package.html (.../package.html) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -10,8 +10,8 @@


-See the Quartz project - at Open Symphony for more information. +See the Quartz project + for more information. Index: 3rdParty_sources/quartz/org/quartz/utils/CircularLossyQueue.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/CircularLossyQueue.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/CircularLossyQueue.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,125 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +/** + * An implementation of a CircularQueue data-structure. + * When the number of items added exceeds the maximum capacity, items that were + * added first are lost. + * + * @param + * Type of the item's to add in this queue + * + * @author Abhishek Sanoujam + * @since 1.7 + */ +public class CircularLossyQueue { + private final AtomicReference[] circularArray; + private final int maxSize; + + private final AtomicLong currentIndex = new AtomicLong(-1); + + /** + * Constructs the circular queue with the specified capacity + * + * @param size + */ + @SuppressWarnings("unchecked") + public CircularLossyQueue(int size) { + this.circularArray = new AtomicReference[size]; + for (int i = 0; i < size; i++) { + this.circularArray[i] = new AtomicReference(); + } + this.maxSize = size; + } + + /** + * Adds a new item + * + * @param newVal + */ + public void push(T newVal) { + int index = (int) (currentIndex.incrementAndGet() % maxSize); + circularArray[index].set(newVal); + } + + /** + * Returns an array of the current elements in the queue. The order of + * elements is in reverse order of the order items were added. + * + * @param type + * @return An array containing the current elements in the queue. The first + * element of the array is the tail of the queue and the last + * element is the head of the queue + */ + public T[] toArray(T[] type) { + System.getProperties(); + + if (type.length > maxSize) { + throw new IllegalArgumentException("Size of array passed in cannot be greater than " + maxSize); + } + + int curIndex = getCurrentIndex(); + for (int k = 0; k < type.length; k++) { + int index = getIndex(curIndex - k); + type[k] = circularArray[index].get(); + } + return type; + } + + private int getIndex(int index) { + return (index < 0 ? index + maxSize : index); + } + + /** + * Returns value at the tail of the queue + * + * @return Value at the tail of the queue + */ + public T peek() { + if (depth() == 0) { + return null; + } + return circularArray[getIndex(getCurrentIndex())].get(); + } + + /** + * Returns true if the queue is empty, otherwise false + * + * @return true if the queue is empty, false otherwise + */ + public boolean isEmtpy() { + return depth() == 0; + } + + private int getCurrentIndex() { + return (int) (currentIndex.get() % maxSize); + } + + /** + * Returns the number of items currently in the queue + * + * @return the number of items in the queue + */ + public int depth() { + long currInd = currentIndex.get() + 1; + return currInd >= maxSize ? maxSize : (int) currInd; + } +} Index: 3rdParty_sources/quartz/org/quartz/utils/ClassUtils.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/ClassUtils.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/ClassUtils.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,61 @@ +package org.quartz.utils; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Queue; + +public class ClassUtils { + + + public static boolean isAnnotationPresent(Class clazz, Class a) { + for (Class c = clazz; c != null; c = c.getSuperclass()) { + if (c.isAnnotationPresent(a)) + return true; + if(isAnnotationPresentOnInterfaces(c, a)) + return true; + } + return false; + } + + private static boolean isAnnotationPresentOnInterfaces(Class clazz, Class a) { + for(Class i : clazz.getInterfaces()) { + if( i.isAnnotationPresent(a) ) + return true; + if(isAnnotationPresentOnInterfaces(i, a)) + return true; + } + + return false; + } + + public static T getAnnotation(Class clazz, Class aClazz) { + //Check class hierarchy + for (Class c = clazz; c != null; c = c.getSuperclass()) { + T anno = c.getAnnotation(aClazz); + if (anno != null) { + return anno; + } + } + + //Check interfaces (breadth first) + Queue> q = new LinkedList>(); + q.add(clazz); + while (!q.isEmpty()) { + Class c = q.remove(); + if (c != null) { + if (c.isInterface()) { + T anno = c.getAnnotation(aClazz); + if (anno != null) { + return anno; + } + } else { + q.add(c.getSuperclass()); + } + q.addAll(Arrays.asList(c.getInterfaces())); + } + } + + return null; + } +} Index: 3rdParty_sources/quartz/org/quartz/utils/ConnectionProvider.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/utils/ConnectionProvider.java (.../ConnectionProvider.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/utils/ConnectionProvider.java (.../ConnectionProvider.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.utils; import java.sql.Connection; @@ -47,8 +44,10 @@ * @return connection managed by this provider * @throws SQLException */ - public Connection getConnection() throws SQLException; + Connection getConnection() throws SQLException; - public void shutdown() throws SQLException; + void shutdown() throws SQLException; + + void initialize() throws SQLException; } Index: 3rdParty_sources/quartz/org/quartz/utils/DBConnectionManager.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/utils/DBConnectionManager.java (.../DBConnectionManager.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/utils/DBConnectionManager.java (.../DBConnectionManager.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.utils; import java.sql.Connection; @@ -61,7 +58,7 @@ private static DBConnectionManager instance = new DBConnectionManager(); - private HashMap providers = new HashMap(); + private HashMap providers = new HashMap(); /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -102,11 +99,11 @@ * 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 + "'"); + ConnectionProvider provider = providers.get(dsName); + if (provider == null) { + throw new SQLException("There is no DataSource named '" + + dsName + "'"); + } return provider.getConnection(); } @@ -126,7 +123,6 @@ * Shuts down database connections from the DataSource with the given name, * if applicable for the underlying provider. * - * @return a database connection * @exception SQLException * if an error occurs, or there is no DataSource with the * given name. @@ -135,9 +131,10 @@ ConnectionProvider provider = (ConnectionProvider) providers .get(dsName); - if (provider == null) + if (provider == null) { throw new SQLException("There is no DataSource named '" + dsName + "'"); + } provider.shutdown(); Index: 3rdParty_sources/quartz/org/quartz/utils/DirtyFlagMap.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/utils/DirtyFlagMap.java (.../DirtyFlagMap.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/utils/DirtyFlagMap.java (.../DirtyFlagMap.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,28 +1,26 @@ -/* - * 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 +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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.utils; +import java.lang.reflect.Array; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -31,101 +29,74 @@ * An implementation of Map that wraps another Map * and flags itself 'dirty' when it is modified. *

- * + * * @author James House */ -public class DirtyFlagMap implements Map, Cloneable, java.io.Serializable { +public class DirtyFlagMap implements Map, Cloneable, java.io.Serializable { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Data members. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ private static final long serialVersionUID = 1433884852607126222L; private boolean dirty = false; - private transient boolean locked = false; - private Map map; + private Map map; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Constructors. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** *

- * Create a DirtyFlagMap that 'wraps' the given Map. - *

- */ - public DirtyFlagMap(Map mapToWrap) { - if (mapToWrap == null) - throw new IllegalArgumentException("mapToWrap cannot be null!"); - - map = mapToWrap; - } - - /** - *

* Create a DirtyFlagMap that 'wraps' a HashMap. *

- * + * * @see java.util.HashMap */ public DirtyFlagMap() { - map = new HashMap(); + map = new HashMap(); } /** *

* Create a DirtyFlagMap that 'wraps' a HashMap that has the * given initial capacity. *

- * + * * @see java.util.HashMap */ - public DirtyFlagMap(int initialCapacity) { - map = new HashMap(initialCapacity); + public DirtyFlagMap(final int initialCapacity) { + map = new HashMap(initialCapacity); } /** *

* Create a DirtyFlagMap that 'wraps' a HashMap that has the * given initial capacity and load factor. *

- * + * * @see java.util.HashMap */ - public DirtyFlagMap(int initialCapacity, float loadFactor) { - map = new HashMap(initialCapacity, loadFactor); + public DirtyFlagMap(final int initialCapacity, final float loadFactor) { + map = new HashMap(initialCapacity, loadFactor); } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * + * * Interface. - * + * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - - public void setMutable(boolean mutable) { - this.locked = !mutable; - if(locked) - map = Collections.unmodifiableMap(map); - else - map = new HashMap(map); - } - - - public boolean isMutable() { - return !locked; - } - /** *

* Clear the 'dirty' flag (set dirty flag to false). @@ -149,62 +120,76 @@ * Get a direct handle to the underlying Map. *

*/ - public Map getWrappedMap() { + public Map getWrappedMap() { return map; } public void clear() { - dirty = true; - + if (!map.isEmpty()) { + dirty = true; + } map.clear(); } - public boolean containsKey(Object key) { + public boolean containsKey(final Object key) { return map.containsKey(key); } - public boolean containsValue(Object val) { + public boolean containsValue(final Object val) { return map.containsValue(val); } - public Set entrySet() { - return map.entrySet(); + public Set> entrySet() { + return new DirtyFlagMapEntrySet(map.entrySet()); } - public boolean equals(Object obj) { - if (obj == null || !(obj instanceof DirtyFlagMap)) return false; + @Override + public boolean equals(final Object obj) { + if (obj == null || !(obj instanceof DirtyFlagMap)) { + return false; + } - return map.equals(((DirtyFlagMap) obj).getWrappedMap()); + return map.equals(((DirtyFlagMap) obj).getWrappedMap()); } - public Object get(Object key) { + @Override + public int hashCode() + { + return map.hashCode(); + } + + public V get(final Object key) { return map.get(key); } public boolean isEmpty() { return map.isEmpty(); } - public Set keySet() { - return map.keySet(); + public Set keySet() { + return new DirtyFlagSet(map.keySet()); } - public Object put(Object key, Object val) { + public V put(final K key, final V val) { dirty = true; return map.put(key, val); } - public void putAll(Map t) { - if (!t.isEmpty()) dirty = true; + public void putAll(final Map t) { + if (!t.isEmpty()) { + dirty = true; + } map.putAll(t); } - public Object remove(Object key) { - Object obj = map.remove(key); + public V remove(final Object key) { + V obj = map.remove(key); - if (obj != null) dirty = true; + if (obj != null) { + dirty = true; + } return obj; } @@ -213,21 +198,204 @@ return map.size(); } - public Collection values() { - return map.values(); + public Collection values() { + return new DirtyFlagCollection(map.values()); } + @Override + @SuppressWarnings("unchecked") // suppress warnings on generic cast of super.clone() and map.clone() lines. public Object clone() { - DirtyFlagMap copy; + DirtyFlagMap copy; try { - copy = (DirtyFlagMap) super.clone(); - if (map instanceof HashMap) - copy.map = (Map) ((HashMap) map).clone(); + copy = (DirtyFlagMap) super.clone(); + if (map instanceof HashMap) { + copy.map = (Map)((HashMap)map).clone(); + } } catch (CloneNotSupportedException ex) { throw new IncompatibleClassChangeError("Not Cloneable."); } return copy; } -} \ No newline at end of file + /** + * 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(final Collection c) { + collection = c; + } + + protected Collection getWrappedCollection() { + return collection; + } + + public Iterator iterator() { + return new DirtyFlagIterator(collection.iterator()); + } + + public boolean remove(final Object o) { + boolean removed = collection.remove(o); + if (removed) { + dirty = true; + } + return removed; + } + + public boolean removeAll(final Collection c) { + boolean changed = collection.removeAll(c); + if (changed) { + dirty = true; + } + return changed; + } + + public boolean retainAll(final 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(final Object o) { return collection.contains(o); } + public boolean add(final T o) { return collection.add(o); } // Not supported + public boolean addAll(final Collection c) { return collection.addAll(c); } // Not supported + public boolean containsAll(final Collection c) { return collection.containsAll(c); } + public Object[] toArray() { return collection.toArray(); } + public U[] toArray(final U[] 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(final 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(final Iterator iterator) { + this.iterator = iterator; + } + + public void remove() { + dirty = true; + iterator.remove(); + } + + // Pure wrapper methods + public boolean hasNext() { return iterator.hasNext(); } + public T 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 the DirtyFlagMapEntry class. + */ + private class DirtyFlagMapEntrySet extends DirtyFlagSet> { + + public DirtyFlagMapEntrySet(final Set> set) { + super(set); + } + + @Override + public Iterator> iterator() { + return new DirtyFlagMapEntryIterator(getWrappedSet().iterator()); + } + + @Override + public Object[] toArray() { + return toArray(new Object[super.size()]); + } + + @SuppressWarnings("unchecked") // suppress warnings on both U[] and U casting. + @Override + public U[] toArray(final U[] 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(); + + U[] result = + array.length < size ? + (U[])Array.newInstance(array.getClass().getComponentType(), size) : array; + + Iterator> entryIter = iterator(); // Will return DirtyFlagMapEntry objects + for (int i = 0; i < size; i++) { + result[i] = ( U ) 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(final Iterator> iterator) { + super(iterator); + } + + @Override + public DirtyFlagMapEntry next() { + return new DirtyFlagMapEntry(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(final Map.Entry entry) { + this.entry = entry; + } + + public V setValue(final V o) { + dirty = true; + return entry.setValue(o); + } + + // Pure wrapper methods + public K getKey() { return entry.getKey(); } + public V getValue() { return entry.getValue(); } + public boolean equals(Object o) { return entry.equals(o); } + } +} + Index: 3rdParty_sources/quartz/org/quartz/utils/FindbugsSuppressWarnings.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/FindbugsSuppressWarnings.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/FindbugsSuppressWarnings.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,30 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.quartz.utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, + ElementType.LOCAL_VARIABLE, ElementType.PACKAGE }) +@Retention(RetentionPolicy.CLASS) +public @interface FindbugsSuppressWarnings { + + String[] value() default {}; +} Index: 3rdParty_sources/quartz/org/quartz/utils/JNDIConnectionProvider.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/utils/JNDIConnectionProvider.java (.../JNDIConnectionProvider.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/utils/JNDIConnectionProvider.java (.../JNDIConnectionProvider.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.utils; import java.sql.Connection; @@ -29,8 +26,8 @@ import javax.sql.DataSource; import javax.sql.XADataSource; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** *

@@ -66,6 +63,8 @@ private boolean alwaysLookup = false; + private final Logger log = LoggerFactory.getLogger(getClass()); + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -111,27 +110,25 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - Log getLog() { - return LogFactory.getLog(getClass()); + protected Logger getLog() { + return log; } private void init() { if (!isAlwaysLookup()) { Context ctx = null; try { - if (props != null) ctx = new InitialContext(props); - else - ctx = new InitialContext(); + 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) + } finally { + if (ctx != null) { try { ctx.close(); } catch(Exception ignore) {} + } } } } @@ -142,34 +139,34 @@ Object ds = this.datasource; if (ds == null || isAlwaysLookup()) { - if (props != null) ctx = new InitialContext(props); - else - ctx = new InitialContext(); + ctx = (props != null) ? new InitialContext(props): new InitialContext(); ds = ctx.lookup(url); - if (!isAlwaysLookup()) this.datasource = ds; + if (!isAlwaysLookup()) { + this.datasource = ds; + } } - if (ds == null) - throw new SQLException( - "There is no object at the JNDI URL '" + url + "'"); + 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."); + 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) + } finally { + if (ctx != null) { try { ctx.close(); } catch(Exception ignore) {} + } } } @@ -188,4 +185,7 @@ // do nothing } + public void initialize() throws SQLException { + // do nothing, already initialized during constructor call + } } Index: 3rdParty_sources/quartz/org/quartz/utils/Key.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/utils/Key.java (.../Key.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/utils/Key.java (.../Key.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,20 +15,32 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.utils; +import java.io.Serializable; +import java.util.UUID; + + /** *

* Object representing a job or trigger key. *

* * @author Jeffrey Wescott */ -public class Key extends Pair { +public class Key implements Serializable, Comparable> { + + private static final long serialVersionUID = -7141167957642391350L; + /** + * The default group for scheduling entities, with the value "DEFAULT". + */ + public static final String DEFAULT_GROUP = "DEFAULT"; + + private final String name; + private final String group; + + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -46,9 +58,13 @@ * the group */ public Key(String name, String group) { - super(); - super.setFirst(name); - super.setSecond(group); + if(name == null) + throw new IllegalArgumentException("Name cannot be null."); + this.name = name; + if(group != null) + this.group = group; + else + this.group = DEFAULT_GROUP; } /* @@ -67,7 +83,7 @@ * @return the name */ public String getName() { - return (String) getFirst(); + return name; } /** @@ -78,7 +94,7 @@ * @return the group */ public String getGroup() { - return (String) getSecond(); + return group; } /** @@ -89,9 +105,64 @@ * * @return the string representation of the key */ + @Override public String toString() { return getGroup() + '.' + getName(); } -} -// EOF + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((group == null) ? 0 : group.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + @SuppressWarnings("unchecked") + Key other = (Key) obj; + if (group == null) { + if (other.group != null) + return false; + } else if (!group.equals(other.group)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + + public int compareTo(Key o) { + + if(group.equals(DEFAULT_GROUP) && !o.group.equals(DEFAULT_GROUP)) + return -1; + if(!group.equals(DEFAULT_GROUP) && o.group.equals(DEFAULT_GROUP)) + return 1; + + int r = group.compareTo(o.getGroup()); + if(r != 0) + return r; + + return name.compareTo(o.getName()); + } + + public static String createUniqueName(String group) { + if(group == null) + group = DEFAULT_GROUP; + + String n1 = UUID.randomUUID().toString(); + String n2 = UUID.nameUUIDFromBytes(group.getBytes()).toString(); + + return String.format("%s-%s", n2.substring(24), n1); + } +} Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/utils/Pair.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/utils/PoolingConnectionProvider.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/utils/PoolingConnectionProvider.java (.../PoolingConnectionProvider.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/utils/PoolingConnectionProvider.java (.../PoolingConnectionProvider.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,27 +15,26 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.utils; +import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; -import org.apache.commons.dbcp.BasicDataSource; +import org.quartz.SchedulerException; +import com.mchange.v2.c3p0.ComboPooledDataSource; + /** *

- * A ConnectionProvider implementation that creates it's own + * A ConnectionProvider implementation that creates its own * pool of connections. *

* *

- * This class uses DBCP - * , an Apache-Jakarta-Commons product. - *

+ * This class uses C3PO (http://www.mchange.com/projects/c3p0/index.html) as + * the underlying pool implementation.

* * @see DBConnectionManager * @see ConnectionProvider @@ -54,26 +53,58 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - public static final String DB_PROPS_PREFIX = "org.quartz.db."; - - public static final String DB_JNDI_DATASOURCE_URL = "jndiURL"; - - // The JDBC database driver + /** The JDBC database driver. */ public static final String DB_DRIVER = "driver"; - // The JDBC database URL + /** The JDBC database URL. */ public static final String DB_URL = "URL"; - // The database user name + /** The database user name. */ public static final String DB_USER = "user"; - // The database user password + /** The database user password. */ public static final String DB_PASSWORD = "password"; + /** The maximum number of database connections to have in the pool. Default is 10. */ public static final String DB_MAX_CONNECTIONS = "maxConnections"; + /** + * The maximum number of prepared statements that will be cached per connection in the pool. + * Depending upon your JDBC Driver this may significantly help performance, or may slightly + * hinder performance. + * Default is 120, as Quartz uses over 100 unique statements. 0 disables the feature. + */ + public static final String DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = "maxCachedStatementsPerConnection"; + + /** + * The database sql query to execute every time a connection is returned + * to the pool to ensure that it is still valid. + */ public static final String DB_VALIDATION_QUERY = "validationQuery"; + /** + * The number of seconds between tests of idle connections - only enabled + * if the validation query property is set. Default is 50 seconds. + */ + public static final String DB_IDLE_VALIDATION_SECONDS = "idleConnectionValidationSeconds"; + + /** + * Whether the database sql query to validate connections should be executed every time + * a connection is retrieved from the pool to ensure that it is still valid. If false, + * then validation will occur on check-in. Default is false. + */ + public static final String DB_VALIDATE_ON_CHECKOUT = "validateOnCheckout"; + + /** Discard connections after they have been idle this many seconds. 0 disables the feature. Default is 0.*/ + private static final String DB_DISCARD_IDLE_CONNECTIONS_SECONDS = "discardIdleConnectionsSeconds"; + + /** Default maximum number of database connections in the pool. */ + public static final int DEFAULT_DB_MAX_CONNECTIONS = 10; + + /** Default maximum number of database connections in the pool. */ + public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120; + + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -82,7 +113,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private BasicDataSource datasource; + private ComboPooledDataSource datasource; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -94,85 +125,133 @@ public PoolingConnectionProvider(String dbDriver, String dbURL, String dbUser, String dbPassword, int maxConnections, - String dbValidationQuery) throws SQLException { - - initialize(dbDriver, dbURL, dbUser, dbPassword, maxConnections, - dbValidationQuery); - + String dbValidationQuery) throws SQLException, SchedulerException { + initialize( + dbDriver, dbURL, dbUser, dbPassword, + maxConnections, DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION, dbValidationQuery, false, 50, 0); } - /* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Interface. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - - private void initialize(String dbDriver, String dbURL, String dbUser, - String dbPassword, int maxConnections, String dbValidationQuery) - throws SQLException { - if (dbDriver == null) - throw new SQLException("DB driver class name cannot be null!"); - if (dbURL == null) throw new SQLException("DB URL cannot be null!"); - if (maxConnections < 0) - throw new SQLException( - "Max connections must be greater than zero!"); - - datasource = new BasicDataSource(); - datasource.setDriverClassName(dbDriver); - datasource.setUrl(dbURL); - datasource.setUsername(dbUser); - datasource.setPassword(dbPassword); - datasource.setMaxActive(maxConnections); - if (dbValidationQuery != null) - datasource.setValidationQuery(dbValidationQuery); - } - /** - *

* Create a connection pool using the given properties. - *

* *

- * The properties passed should contain either + * The properties passed should contain: *

    - *
  • JNDI DataSource URL {@link #DB_JNDI_DATASOURCE_URL} - *
- * or - *
    *
  • {@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 + *
  • {@link #DB_MAX_CONNECTIONS}- The maximum # connections in the pool, + * optional + *
  • {@link #DB_VALIDATION_QUERY}- The sql validation query, optional *
- *

+ *

* * @param config - * configuration properties - * @exception SQLException - * if an error occurs + * configuration properties */ - public PoolingConnectionProvider(Properties config) throws SQLException { + public PoolingConnectionProvider(Properties config) throws SchedulerException, SQLException { PropertiesParser cfg = new PropertiesParser(config); - String url = config.getProperty(DB_URL); + 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.getIntProperty(DB_MAX_CACHED_STATEMENTS_PER_CONNECTION, DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION), + cfg.getStringProperty(DB_VALIDATION_QUERY), + cfg.getBooleanProperty(DB_VALIDATE_ON_CHECKOUT, false), + cfg.getIntProperty(DB_IDLE_VALIDATION_SECONDS, 50), + cfg.getIntProperty(DB_DISCARD_IDLE_CONNECTIONS_SECONDS, 0)); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Create the underlying C3PO ComboPooledDataSource with the + * default supported properties. + * @throws SchedulerException + */ + private void initialize( + String dbDriver, + String dbURL, + String dbUser, + String dbPassword, + int maxConnections, + int maxStatementsPerConnection, + String dbValidationQuery, + boolean validateOnCheckout, + int idleValidationSeconds, + int maxIdleSeconds) throws SQLException, SchedulerException { + 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 ComboPooledDataSource(); try { - initialize(config.getProperty(DB_DRIVER), url, config - .getProperty(DB_USER), config.getProperty(DB_PASSWORD), cfg - .getIntProperty(DB_MAX_CONNECTIONS, 3), cfg - .getStringProperty(DB_VALIDATION_QUERY)); - } catch (Exception e) { - throw new SQLException("DBPool '" + url - + "' could not be created: " + e.toString()); + datasource.setDriverClass(dbDriver); + } catch (PropertyVetoException e) { + throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e); + } + datasource.setJdbcUrl(dbURL); + datasource.setUser(dbUser); + datasource.setPassword(dbPassword); + datasource.setMaxPoolSize(maxConnections); + datasource.setMinPoolSize(1); + datasource.setMaxIdleTime(maxIdleSeconds); + datasource.setMaxStatementsPerConnection(maxStatementsPerConnection); + + if (dbValidationQuery != null) { + datasource.setPreferredTestQuery(dbValidationQuery); + if(!validateOnCheckout) + datasource.setTestConnectionOnCheckin(true); + else + datasource.setTestConnectionOnCheckout(true); + datasource.setIdleConnectionTestPeriod(idleValidationSeconds); } } + + /** + * Get the C3PO ComboPooledDataSource created during initialization. + * + *

+ * This can be used to set additional data source properties in a + * subclass's constructor. + *

+ */ + protected ComboPooledDataSource getDataSource() { + return datasource; + } public Connection getConnection() throws SQLException { - return this.datasource.getConnection(); + return datasource.getConnection(); } public void shutdown() throws SQLException { - this.datasource.close(); + datasource.close(); } + + public void initialize() throws SQLException { + // do nothing, already initialized during constructor call + } } Index: 3rdParty_sources/quartz/org/quartz/utils/PropertiesParser.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/utils/PropertiesParser.java (.../PropertiesParser.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/utils/PropertiesParser.java (.../PropertiesParser.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,16 +15,13 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.utils; +import java.util.ArrayList; import java.util.Enumeration; -import java.util.HashMap; +import java.util.HashSet; import java.util.Properties; import java.util.StringTokenizer; -import java.util.Vector; /** *

@@ -69,18 +66,29 @@ 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) { - String val = props.getProperty(name); - if (val == null) return null; - return val.trim(); + 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; + if (val == null) { + return def; + } + val = val.trim(); - if (val.length() == 0) return def; - return val; + + return (val.length() == 0) ? def : val; } public String[] getStringArrayProperty(String name) { @@ -89,44 +97,38 @@ public String[] getStringArrayProperty(String name, String[] def) { String vals = getStringProperty(name); - if (vals == null) return def; + if (vals == null) { + return def; + } - if (vals != null && !vals.trim().equals("")) { - StringTokenizer stok = new StringTokenizer(vals, ","); - Vector strs = new Vector(); - try { - while (stok.hasMoreTokens()) { - strs.addElement(stok.nextToken()); - } - String[] outStrs = new String[strs.size()]; - for (int i = 0; i < strs.size(); i++) - outStrs[i] = (String) strs.elementAt(i); - return outStrs; - } catch (Exception e) { - 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; } - - return def; } public boolean getBooleanProperty(String name) { - String val = getStringProperty(name); - if (val == null) return false; - - return new Boolean(val).booleanValue(); + return getBooleanProperty(name, false); } public boolean getBooleanProperty(String name, boolean def) { String val = getStringProperty(name); - if (val == null) return def; - - return new Boolean(val).booleanValue(); + + 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"); + if (val == null) { + throw new NumberFormatException(" null string"); + } try { return Byte.parseByte(val); @@ -136,9 +138,11 @@ } public byte getByteProperty(String name, byte def) - throws NumberFormatException { + throws NumberFormatException { String val = getStringProperty(name); - if (val == null) return def; + if (val == null) { + return def; + } try { return Byte.parseByte(val); @@ -148,26 +152,19 @@ } public char getCharProperty(String name) { - String param = getStringProperty(name); - if (param == null) return '\0'; - - if (param.length() == 0) return '\0'; - - return param.charAt(0); + return getCharProperty(name, '\0'); } public char getCharProperty(String name, char def) { String param = getStringProperty(name); - if (param == null) return def; - - if (param.length() == 0) return def; - - return param.charAt(0); + 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"); + if (val == null) { + throw new NumberFormatException(" null string"); + } try { return Double.parseDouble(val); @@ -177,9 +174,11 @@ } public double getDoubleProperty(String name, double def) - throws NumberFormatException { + throws NumberFormatException { String val = getStringProperty(name); - if (val == null) return def; + if (val == null) { + return def; + } try { return Double.parseDouble(val); @@ -190,7 +189,9 @@ public float getFloatProperty(String name) throws NumberFormatException { String val = getStringProperty(name); - if (val == null) throw new NumberFormatException(" null string"); + if (val == null) { + throw new NumberFormatException(" null string"); + } try { return Float.parseFloat(val); @@ -200,9 +201,11 @@ } public float getFloatProperty(String name, float def) - throws NumberFormatException { + throws NumberFormatException { String val = getStringProperty(name); - if (val == null) return def; + if (val == null) { + return def; + } try { return Float.parseFloat(val); @@ -213,7 +216,9 @@ public int getIntProperty(String name) throws NumberFormatException { String val = getStringProperty(name); - if (val == null) throw new NumberFormatException(" null string"); + if (val == null) { + throw new NumberFormatException(" null string"); + } try { return Integer.parseInt(val); @@ -223,9 +228,11 @@ } public int getIntProperty(String name, int def) - throws NumberFormatException { + throws NumberFormatException { String val = getStringProperty(name); - if (val == null) return def; + if (val == null) { + return def; + } try { return Integer.parseInt(val); @@ -239,36 +246,38 @@ } public int[] getIntArrayProperty(String name, int[] def) - throws NumberFormatException { + throws NumberFormatException { String vals = getStringProperty(name); - if (vals == null) return def; + if (vals == null) { + return def; + } - if (vals != null && !vals.trim().equals("")) { - StringTokenizer stok = new StringTokenizer(vals, ","); - Vector ints = new Vector(); - try { - while (stok.hasMoreTokens()) { - try { - ints.addElement(new Integer(stok.nextToken())); - } catch (NumberFormatException nfe) { - throw new NumberFormatException(" '" + vals + "'"); - } + 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.elementAt(i)).intValue(); - return outInts; - } catch (Exception e) { - return def; } + + 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; } - - return def; } public long getLongProperty(String name) throws NumberFormatException { String val = getStringProperty(name); - if (val == null) throw new NumberFormatException(" null string"); + if (val == null) { + throw new NumberFormatException(" null string"); + } try { return Long.parseLong(val); @@ -278,9 +287,11 @@ } public long getLongProperty(String name, long def) - throws NumberFormatException { + throws NumberFormatException { String val = getStringProperty(name); - if (val == null) return def; + if (val == null) { + return def; + } try { return Long.parseLong(val); @@ -291,7 +302,9 @@ public short getShortProperty(String name) throws NumberFormatException { String val = getStringProperty(name); - if (val == null) throw new NumberFormatException(" null string"); + if (val == null) { + throw new NumberFormatException(" null string"); + } try { return Short.parseShort(val); @@ -301,9 +314,11 @@ } public short getShortProperty(String name, short def) - throws NumberFormatException { + throws NumberFormatException { String val = getStringProperty(name); - if (val == null) return def; + if (val == null) { + return def; + } try { return Short.parseShort(val); @@ -313,40 +328,76 @@ } public String[] getPropertyGroups(String prefix) { - Enumeration keys = props.propertyNames(); - HashMap groups = new HashMap(10); + Enumeration keys = props.propertyNames(); + HashSet groups = new HashSet(10); - if (!prefix.endsWith(".")) prefix += "."; + 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.put(groupName, groupName); + groups.add(groupName); } } - return (String[]) groups.values().toArray(new String[groups.size()]); + return (String[]) groups.toArray(new String[groups.size()]); } public Properties getPropertyGroup(String prefix) { - return getPropertyGroup(prefix, false); + return getPropertyGroup(prefix, false, null); } public Properties getPropertyGroup(String prefix, boolean stripPrefix) { - Enumeration keys = props.propertyNames(); + 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 += "."; + if (!prefix.endsWith(".")) { + prefix += "."; + } while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); if (key.startsWith(prefix)) { - if (stripPrefix) group.put(key.substring(prefix.length()), - props.getProperty(key)); - else - group.put(key, props.getProperty(key)); + + 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); + } + } } } Index: 3rdParty_sources/quartz/org/quartz/utils/StringKeyDirtyFlagMap.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/StringKeyDirtyFlagMap.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/StringKeyDirtyFlagMap.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,381 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. + * + * 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 org.quartz.utils; + +import java.io.Serializable; + +/** + *

+ * 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); + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public int hashCode() + { + return getWrappedMap().hashCode(); + } + + /** + * Get a copy of the Map's String keys in an array of Strings. + */ + public String[] getKeys() { + return 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]); + } + } + } + + // Due to Generic enforcement, this override method is no longer needed. +// /** +// *

+// * Adds the name-value pairs in the given Map to the +// * StringKeyDirtyFlagMap. +// *

+// * +// *

+// * All keys must be Strings. +// *

+// */ +// @Override +// 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, Integer.valueOf(value)); + } + + /** + *

+ * Adds the given long value to the StringKeyDirtyFlagMap. + *

+ */ + public void put(String key, long value) { + super.put(key, Long.valueOf(value)); + } + + /** + *

+ * Adds the given float value to the StringKeyDirtyFlagMap. + *

+ */ + public void put(String key, float value) { + super.put(key, Float.valueOf(value)); + } + + /** + *

+ * Adds the given double value to the StringKeyDirtyFlagMap. + *

+ */ + public void put(String key, double value) { + super.put(key, Double.valueOf(value)); + } + + /** + *

+ * Adds the given boolean value to the StringKeyDirtyFlagMap. + *

+ */ + public void put(String key, boolean value) { + super.put(key, Boolean.valueOf(value)); + } + + /** + *

+ * Adds the given char value to the StringKeyDirtyFlagMap. + *

+ */ + public void put(String key, char value) { + super.put(key, Character.valueOf(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. + *

+ */ + @Override + public Object put(String key, Object value) { + return super.put((String)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 { + if(obj instanceof Integer) + return ((Integer) obj).intValue(); + return Integer.parseInt((String)obj); + } 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 { + if(obj instanceof Long) + return ((Long) obj).longValue(); + return Long.parseLong((String)obj); + } 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 { + if(obj instanceof Float) + return ((Float) obj).floatValue(); + return Float.parseFloat((String)obj); + } 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 { + if(obj instanceof Double) + return ((Double) obj).doubleValue(); + return Double.parseDouble((String)obj); + } 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 { + if(obj instanceof Boolean) + return ((Boolean) obj).booleanValue(); + return Boolean.parseBoolean((String)obj); + } 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 { + if(obj instanceof Character) + return ((Character) obj).charValue(); + return ((String)obj).charAt(0); + } 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."); + } + } +} Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/utils/TriggerStatus.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/utils/UpdateChecker.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/UpdateChecker.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/UpdateChecker.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,171 @@ +package org.quartz.utils; +/** + * Copyright 2003-2010 Terracotta, Inc. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.Properties; +import java.util.TimerTask; + +import org.quartz.core.QuartzScheduler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Check for updates and alert users if an update is available + * + * @author Hung Huynh + */ +public class UpdateChecker extends TimerTask { + private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class); + private static final long MILLIS_PER_SECOND = 1000L; + private static final String UNKNOWN = "UNKNOWN"; + private static final String UPDATE_CHECK_URL = "http://www.terracotta.org/kit/reflector?kitID=quartz&pageID=update.properties"; + private static final long START_TIME = System.currentTimeMillis(); + private static final String PRODUCT_NAME = "Quartz"; + + /** + * Run the update check + */ + @Override + public void run() { + checkForUpdate(); + } + + /** + * This method ensures that there will be no exception thrown. + */ + public void checkForUpdate() { + try { + doCheck(); + } catch (Throwable t) { + LOG.debug("Quartz version update check failed: " + t.getMessage()); + } + } + + private void doCheck() throws IOException { + LOG.debug("Checking for available updated version of Quartz..."); + URL updateUrl = buildUpdateCheckUrl(); + Properties updateProps = getUpdateProperties(updateUrl); + String currentVersion = getQuartzVersion(); + String propVal = updateProps.getProperty("general.notice"); + if (notBlank(propVal)) { + LOG.info(propVal); + } + propVal = updateProps.getProperty(currentVersion + ".notices"); + if (notBlank(propVal)) { + LOG.info(propVal); + } + propVal = updateProps.getProperty(currentVersion + ".updates"); + if (notBlank(propVal)) { + StringBuilder sb = new StringBuilder(); + String[] newVersions = propVal.split(","); + for (int i = 0; i < newVersions.length; i++) { + String newVersion = newVersions[i].trim(); + if (i > 0) { + sb.append(", "); + } + sb.append(newVersion); + propVal = updateProps.getProperty(newVersion + ".release-notes"); + if (notBlank(propVal)) { + sb.append(" ["); + sb.append(propVal); + sb.append("]"); + } + } + if (sb.length() > 0) { + LOG.info("New Quartz update(s) found: " + sb.toString()); + } + } else { + // Do nothing at all (ever) on no updates found (DEV-3799) + } + } + + private String getQuartzVersion() { + return String.format("%s.%s.%s", QuartzScheduler.getVersionMajor(), QuartzScheduler.getVersionMinor(), + QuartzScheduler.getVersionIteration()); + } + + private Properties getUpdateProperties(URL updateUrl) throws IOException { + URLConnection connection = updateUrl.openConnection(); + connection.setConnectTimeout(3 * 1000); + InputStream in = connection.getInputStream(); + try { + Properties props = new Properties(); + props.load(connection.getInputStream()); + return props; + } finally { + if (in != null) { + in.close(); + } + } + } + + private URL buildUpdateCheckUrl() throws MalformedURLException, UnsupportedEncodingException { + String url = System.getProperty("quartz.update-check.url", UPDATE_CHECK_URL); + String connector = url.indexOf('?') > 0 ? "&" : "?"; + return new URL(url + connector + buildParamsString()); + } + + private String buildParamsString() throws UnsupportedEncodingException { + StringBuilder sb = new StringBuilder(); + sb.append("id="); + sb.append(getClientId()); + sb.append("&os-name="); + sb.append(urlEncode(getProperty("os.name"))); + sb.append("&jvm-name="); + sb.append(urlEncode(getProperty("java.vm.name"))); + sb.append("&jvm-version="); + sb.append(urlEncode(getProperty("java.version"))); + sb.append("&platform="); + sb.append(urlEncode(getProperty("os.arch"))); + sb.append("&tc-version="); + sb.append(urlEncode(getQuartzVersion())); + sb.append("&tc-product="); + sb.append(urlEncode(PRODUCT_NAME)); + sb.append("&source="); + sb.append(urlEncode(PRODUCT_NAME)); + sb.append("&uptime-secs="); + sb.append(getUptimeInSeconds()); + sb.append("&patch="); + sb.append(urlEncode(UNKNOWN)); + return sb.toString(); + } + + private long getUptimeInSeconds() { + long uptime = System.currentTimeMillis() - START_TIME; + return uptime > 0 ? (uptime / MILLIS_PER_SECOND) : 0; + } + + private int getClientId() { + try { + return InetAddress.getLocalHost().hashCode(); + } catch (Throwable t) { + return 0; + } + } + + private String urlEncode(String param) throws UnsupportedEncodingException { + return URLEncoder.encode(param, "UTF-8"); + } + + private String getProperty(String prop) { + return System.getProperty(prop, UNKNOWN); + } + + private boolean notBlank(String s) { + return s != null && s.trim().length() > 0; + } + + public static void main(String[] args) { + new UpdateChecker().run(); + } +} + Index: 3rdParty_sources/quartz/org/quartz/utils/counter/Counter.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/Counter.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/Counter.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,80 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter; + +/** + * A simple counter + * + * @author Abhishek Sanoujam + * + * @since 1.8 + */ +public interface Counter { + + /** + * Increment the counter by 1 + * + * @return the value after incrementing + */ + long increment(); + + /** + * Decrement the counter by 1 + * + * @return the value after decrementing + */ + long decrement(); + + /** + * Returns the value of the counter and sets it to the new value + * + * @param newValue + * @return Returns the old value + */ + long getAndSet(long newValue); + + /** + * Gets current value of the counter + * + * @return current value of the counter + */ + long getValue(); + + /** + * Increment the counter by given amount + * + * @param amount + * @return the value of the counter after incrementing + */ + long increment(long amount); + + /** + * Decrement the counter by given amount + * + * @param amount + * @return the value of the counter after decrementing + */ + long decrement(long amount); + + /** + * Sets the value of the counter to the supplied value + * + * @param newValue + */ + void setValue(long newValue); + +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/CounterConfig.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/CounterConfig.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/CounterConfig.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,56 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter; + +/** + * Config for a simple Counter + * + * @author Abhishek Sanoujam + * @since 1.8 + * + */ +public class CounterConfig { + + private final long initialValue; + + /** + * Creates a config with the initial value + * + * @param initialValue + */ + public CounterConfig(long initialValue) { + this.initialValue = initialValue; + } + + /** + * Gets the initial value + * + * @return the initial value of counters created by this config + */ + public final long getInitialValue() { + return initialValue; + } + + /** + * Creates and returns a Counter based on the initial value + * + * @return The counter created by this config + */ + public Counter createCounter() { + return new CounterImpl(initialValue); + } +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/CounterImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/CounterImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/CounterImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,100 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter; + +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicLong; + +/** + * A simple counter implementation + * + * @author Abhishek Sanoujam + * @since 1.8 + * + */ +public class CounterImpl implements Counter, Serializable { + + private static final long serialVersionUID = -1529134342654953984L; + + private AtomicLong value; + + /** + * Default Constructor + */ + public CounterImpl() { + this(0L); + } + + /** + * Constructor with initial value + * + * @param initialValue + */ + public CounterImpl(long initialValue) { + this.value = new AtomicLong(initialValue); + } + + /** + * {@inheritDoc} + */ + public long increment() { + return value.incrementAndGet(); + } + + /** + * {@inheritDoc} + */ + public long decrement() { + return value.decrementAndGet(); + } + + /** + * {@inheritDoc} + */ + public long getAndSet(long newValue) { + return value.getAndSet(newValue); + } + + /** + * {@inheritDoc} + */ + public long getValue() { + return value.get(); + } + + /** + * {@inheritDoc} + */ + public long increment(long amount) { + return value.addAndGet(amount); + } + + /** + * {@inheritDoc} + */ + public long decrement(long amount) { + return value.addAndGet(amount * -1); + } + + /** + * {@inheritDoc} + */ + public void setValue(long newValue) { + value.set(newValue); + } + +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/CounterManager.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/CounterManager.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/CounterManager.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,48 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter; + +/** + * A Counter Manager that accepts a config to create counters. Creates counter's + * based on {@link CounterConfig}. This manages the lifycycle of a counter + * + * @author Abhishek Sanoujam + * @since 1.8 + * + */ +public interface CounterManager { + /** + * Creates a Counter based on tha passed config + * + * @param config + * @return The counter created and managed by this CounterManager + */ + Counter createCounter(CounterConfig config); + + /** + * Shuts down this counter manager + */ + void shutdown(boolean killTimer); + + /** + * Shuts down the counter + * + * @param counter + */ + void shutdownCounter(Counter counter); + +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/CounterManagerImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/CounterManagerImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/CounterManagerImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,100 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; + +import org.quartz.utils.counter.sampled.SampledCounter; +import org.quartz.utils.counter.sampled.SampledCounterImpl; + +/** + * An implementation of a {@link CounterManager}. + * + * @author Abhishek Sanoujam + * @since 1.8 + * + */ +public class CounterManagerImpl implements CounterManager { + + private Timer timer; + private boolean shutdown; + private List counters = new ArrayList(); + + /** + * Constructor that accepts a timer that will be used for scheduling sampled + * counter if any is created + */ + public CounterManagerImpl(Timer timer) { + if (timer == null) { + throw new IllegalArgumentException("Timer cannot be null"); + } + this.timer = timer; + } + + /** + * {@inheritDoc} + */ + public synchronized void shutdown(boolean killTimer) { + if (shutdown) { + return; + } + try { + // shutdown the counters of this counterManager + for (Counter counter : counters) { + if (counter instanceof SampledCounter) { + ((SampledCounter) counter).shutdown(); + } + } + if(killTimer) + timer.cancel(); + } finally { + shutdown = true; + } + } + + /** + * {@inheritDoc} + */ + public synchronized Counter createCounter(CounterConfig config) { + if (shutdown) { + throw new IllegalStateException("counter manager is shutdown"); + } + if (config == null) { + throw new NullPointerException("config cannot be null"); + } + Counter counter = config.createCounter(); + if (counter instanceof SampledCounterImpl) { + SampledCounterImpl sampledCounter = (SampledCounterImpl) counter; + timer.schedule(sampledCounter.getTimerTask(), sampledCounter.getIntervalMillis(), sampledCounter.getIntervalMillis()); + } + counters.add(counter); + return counter; + } + + /** + * {@inheritDoc} + */ + public void shutdownCounter(Counter counter) { + if (counter instanceof SampledCounter) { + SampledCounter sc = (SampledCounter) counter; + sc.shutdown(); + } + } + +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledCounter.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledCounter.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledCounter.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,55 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter.sampled; + +import org.quartz.utils.counter.Counter; + +/** + * Interface of a sampled counter -- a counter that keeps sampled values + * + * @author Abhishek Sanoujam + * @since 1.8 + * + */ +public interface SampledCounter extends Counter { + /** + * Shutdown this counter + */ + void shutdown(); + + /** + * Returns the most recent sampled value + * + * @return Value of the most recent sampled value + */ + TimeStampedCounterValue getMostRecentSample(); + + /** + * Returns all samples in history + * + * @return An array containing the TimeStampedCounterValue's + */ + TimeStampedCounterValue[] getAllSampleValues(); + + /** + * Returns the current value of the counter and resets it to 0 + * + * @return current value of the counter + */ + long getAndReset(); + +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledCounterConfig.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledCounterConfig.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledCounterConfig.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,93 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter.sampled; + +import org.quartz.utils.counter.Counter; +import org.quartz.utils.counter.CounterConfig; + +/** + * Config for a {@link SampledCounter} + * + * @author Abhishek Sanoujam + * @since 1.7 + * + */ +public class SampledCounterConfig extends CounterConfig { + private final int intervalSecs; + private final int historySize; + private final boolean isReset; + + /** + * Make a new timed counter config (duh) + * + * @param intervalSecs + * the interval (in seconds) between sampling + * @param historySize + * number of counter samples that will be retained in memory + * @param isResetOnSample + * true if the counter should be reset to 0 upon each sample + */ + public SampledCounterConfig(int intervalSecs, int historySize, boolean isResetOnSample, long initialValue) { + super(initialValue); + if (intervalSecs < 1) { + throw new IllegalArgumentException("Interval (" + intervalSecs + ") must be greater than or equal to 1"); + } + if (historySize < 1) { + throw new IllegalArgumentException("History size (" + historySize + ") must be greater than or equal to 1"); + } + + this.intervalSecs = intervalSecs; + this.historySize = historySize; + this.isReset = isResetOnSample; + } + + /** + * Returns the history size + * + * @return The history size + */ + public int getHistorySize() { + return historySize; + } + + /** + * Returns the interval time (seconds) + * + * @return Interval of the sampling thread in seconds + */ + public int getIntervalSecs() { + return intervalSecs; + } + + /** + * Returns true if counters created from this config will reset on each + * sample + * + * @return true if values are reset to the initial value after each sample + */ + public boolean isResetOnSample() { + return this.isReset; + } + + /** + * {@inheritDoc} + */ + @Override + public Counter createCounter() { + return new SampledCounterImpl(this); + } +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledCounterImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledCounterImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledCounterImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,135 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter.sampled; + +import java.util.TimerTask; + +import org.quartz.utils.CircularLossyQueue; +import org.quartz.utils.counter.CounterImpl; + +/** + * An implementation of {@link SampledCounter} + * + * @author Abhishek Sanoujam + * @since 1.7 + * + */ +public class SampledCounterImpl extends CounterImpl implements SampledCounter { + + private static final long serialVersionUID = -3605369302464131521L; + + private static final int MILLIS_PER_SEC = 1000; + + /** + * The history of this counter + */ + protected final CircularLossyQueue history; + + /** + * Should the counter reset on each sample? + */ + protected final boolean resetOnSample; + private final TimerTask samplerTask; + private final long intervalMillis; + + /** + * Constructor accepting a {@link SampledCounterConfig} + * + * @param config + */ + public SampledCounterImpl(SampledCounterConfig config) { + super(config.getInitialValue()); + + this.intervalMillis = config.getIntervalSecs() * MILLIS_PER_SEC; + this.history = new CircularLossyQueue(config.getHistorySize()); + this.resetOnSample = config.isResetOnSample(); + + this.samplerTask = new TimerTask() { + @Override + public void run() { + recordSample(); + } + }; + + recordSample(); + } + + /** + * {@inheritDoc} + */ + public TimeStampedCounterValue getMostRecentSample() { + return this.history.peek(); + } + + /** + * {@inheritDoc} + */ + public TimeStampedCounterValue[] getAllSampleValues() { + return this.history.toArray(new TimeStampedCounterValue[this.history.depth()]); + } + + /** + * {@inheritDoc} + */ + public void shutdown() { + if (samplerTask != null) { + samplerTask.cancel(); + } + } + + /** + * Returns the timer task for this sampled counter + * + * @return the timer task for this sampled counter + */ + public TimerTask getTimerTask() { + return this.samplerTask; + } + + /** + * Returns the sampling thread interval in millis + * + * @return the sampling thread interval in millis + */ + public long getIntervalMillis() { + return intervalMillis; + } + + /** + * {@inheritDoc} + */ + void recordSample() { + final long sample; + if (resetOnSample) { + sample = getAndReset(); + } else { + sample = getValue(); + } + + final long now = System.currentTimeMillis(); + TimeStampedCounterValue timedSample = new TimeStampedCounterValue(now, sample); + + history.push(timedSample); + } + + /** + * {@inheritDoc} + */ + public long getAndReset() { + return getAndSet(0L); + } +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledRateCounter.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledRateCounter.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledRateCounter.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,67 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter.sampled; + +/** + * Interface of a sampled rate counter -- a counter that keeps sampled values of + * rates + * + * @author Abhishek Sanoujam + * @since 1.8 + * + */ +public interface SampledRateCounter extends SampledCounter { + + /** + * Increments the numerator and denominator by the passed values + * + * @param numerator + * @param denominator + */ + public void increment(long numerator, long denominator); + + /** + * Decrements the numerator and denominator by the passed values + * + * @param numerator + * @param denominator + */ + public void decrement(long numerator, long denominator); + + /** + * Sets the values of the numerator and denominator to the passed values + * + * @param numerator + * @param denominator + */ + public void setValue(long numerator, long denominator); + + /** + * Sets the value of the numerator to the passed value + * + * @param newValue + */ + public void setNumeratorValue(long newValue); + + /** + * Sets the value of the denominator to the passed value + * + * @param newValue + */ + public void setDenominatorValue(long newValue); + +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledRateCounterConfig.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledRateCounterConfig.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledRateCounterConfig.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,74 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter.sampled; + +import org.quartz.utils.counter.Counter; + +/** + * An implementation of {@link SampledCounterConfig} + * + * @author Abhishek Sanoujam + * @since 1.8 + * + */ +public class SampledRateCounterConfig extends SampledCounterConfig { + + private final long initialNumeratorValue; + private final long initialDenominatorValue; + + /** + * Constructor accepting the interval time in seconds, history-size and + * whether counters should reset on each sample or not. + * Initial values of both numerator and denominator are zeroes + * + * @param intervalSecs + * @param historySize + * @param isResetOnSample + */ + public SampledRateCounterConfig(int intervalSecs, int historySize, boolean isResetOnSample) { + this(intervalSecs, historySize, isResetOnSample, 0, 0); + } + + /** + * Constructor accepting the interval time in seconds, history-size and + * whether counters should reset on each sample or not. Also the initial + * values for the numerator and the denominator + * + * @param intervalSecs + * @param historySize + * @param isResetOnSample + * @param initialNumeratorValue + * @param initialDenominatorValue + */ + public SampledRateCounterConfig(int intervalSecs, int historySize, boolean isResetOnSample, long initialNumeratorValue, + long initialDenominatorValue) { + super(intervalSecs, historySize, isResetOnSample, 0); + this.initialNumeratorValue = initialNumeratorValue; + this.initialDenominatorValue = initialDenominatorValue; + } + + /** + * {@inheritDoc} + */ + @Override + public Counter createCounter() { + SampledRateCounterImpl sampledRateCounter = new SampledRateCounterImpl(this); + sampledRateCounter.setValue(initialNumeratorValue, initialDenominatorValue); + return sampledRateCounter; + } + +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledRateCounterImpl.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledRateCounterImpl.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/SampledRateCounterImpl.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,164 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter.sampled; + +/** + * An implementation of {@link SampledRateCounter} + * + * @author Abhishek Sanoujam + * @since 1.8 + * + */ +public class SampledRateCounterImpl extends SampledCounterImpl implements SampledRateCounter { + + private static final long serialVersionUID = 6531350452676920607L; + + private static final String OPERATION_NOT_SUPPORTED_MSG = "This operation is not supported. Use SampledCounter Or Counter instead"; + + private long numeratorValue; + private long denominatorValue; + + /** + * Constructor accepting the config + * + * @param config + */ + public SampledRateCounterImpl(SampledRateCounterConfig config) { + super(config); + } + + /** + * {@inheritDoc} + */ + public synchronized void setValue(long numerator, long denominator) { + this.numeratorValue = numerator; + this.denominatorValue = denominator; + } + + /** + * {@inheritDoc} + */ + public synchronized void increment(long numerator, long denominator) { + this.numeratorValue += numerator; + this.denominatorValue += denominator; + } + + /** + * {@inheritDoc} + */ + public synchronized void decrement(long numerator, long denominator) { + this.numeratorValue -= numerator; + this.denominatorValue -= denominator; + } + + /** + * {@inheritDoc} + */ + public synchronized void setDenominatorValue(long newValue) { + this.denominatorValue = newValue; + } + + /** + * {@inheritDoc} + */ + public synchronized void setNumeratorValue(long newValue) { + this.numeratorValue = newValue; + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized long getValue() { + return denominatorValue == 0 ? 0 : (numeratorValue / denominatorValue); + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized long getAndReset() { + long prevVal = getValue(); + setValue(0, 0); + return prevVal; + } + + // ====== unsupported operations. These operations need multiple params for + // this class + /** + * throws {@link UnsupportedOperationException} + */ + @Override + public long getAndSet(long newValue) { + throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED_MSG); + } + + /** + * throws {@link UnsupportedOperationException} + */ + @Override + public synchronized void setValue(long newValue) { + throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED_MSG); + } + + /** + * throws {@link UnsupportedOperationException} + */ + @Override + public long decrement() { + throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED_MSG); + } + + /** + * throws {@link UnsupportedOperationException} + */ + @Override + public long decrement(long amount) { + throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED_MSG); + } + + /** + * throws {@link UnsupportedOperationException} + */ + public long getMaxValue() { + throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED_MSG); + } + + /** + * throws {@link UnsupportedOperationException} + */ + public long getMinValue() { + throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED_MSG); + } + + /** + * throws {@link UnsupportedOperationException} + */ + @Override + public long increment() { + throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED_MSG); + } + + /** + * throws {@link UnsupportedOperationException} + */ + @Override + public long increment(long amount) { + throw new UnsupportedOperationException(OPERATION_NOT_SUPPORTED_MSG); + } + +} Index: 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/TimeStampedCounterValue.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/TimeStampedCounterValue.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/counter/sampled/TimeStampedCounterValue.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,71 @@ +/** + * Copyright 2003-2009 Terracotta, Inc. + * + * 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 org.quartz.utils.counter.sampled; + +import java.io.Serializable; + +/** + * A counter value at a particular time instance + * + * @author Abhishek Sanoujam + * @since 1.8 + */ +public class TimeStampedCounterValue implements Serializable { + + private static final long serialVersionUID = 1931111347823687672L; + + private final long counterValue; + private final long timestamp; + + /** + * Constructor accepting the value of both timestamp and the counter value. + * + * @param timestamp + * @param value + */ + public TimeStampedCounterValue(long timestamp, long value) { + this.timestamp = timestamp; + this.counterValue = value; + } + + /** + * Get the counter value + * + * @return The counter value + */ + public long getCounterValue() { + return this.counterValue; + } + + /** + * Get value of the timestamp + * + * @return the timestamp associated with the current value + */ + public long getTimestamp() { + return this.timestamp; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "value: " + this.counterValue + ", timestamp: " + this.timestamp; + } + +} Index: 3rdParty_sources/quartz/org/quartz/utils/weblogic/WeblogicConnectionProvider.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/utils/weblogic/WeblogicConnectionProvider.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/utils/weblogic/WeblogicConnectionProvider.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,93 @@ +/* + * Copyright 2001-2009 Terracotta, Inc. Inc. + * + * 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 org.quartz.utils.weblogic; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.quartz.utils.ConnectionProvider; + +import weblogic.jdbc.jts.Driver; + +/** + *

+ * Provides connections via Weblogic's JTS driver. + *

+ * + * @see org.quartz.utils.ConnectionProvider + * @see org.quartz.utils.DBConnectionManager + * + * @author Mohammad Rezaei + * @author James House + */ +@SuppressWarnings("deprecation") +public class WeblogicConnectionProvider implements ConnectionProvider { + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + private String poolName; + + private weblogic.jdbc.jts.Driver driver; + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public WeblogicConnectionProvider(String poolName) { + this.poolName = poolName; + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public Connection getConnection() throws SQLException { + return driver.connect("jdbc:weblogic:jts:" + poolName, + (java.util.Properties) null); + } + + public void initialize() throws SQLException { + try { + driver = (Driver) weblogic.jdbc.jts.Driver.class.newInstance(); + } catch (Exception e) { + throw new SQLException( + "Could not get weblogic pool connection with name '" + + poolName + "': " + e.getClass().getName() + ": " + + e.getMessage()); + } + } + + public void shutdown() throws SQLException { + // do nothing + } + +} Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/xml/CalendarBundle.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/xml/JobSchedulingBundle.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/xml/JobSchedulingDataProcessor.java'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/xml/ValidationException.java =================================================================== diff -u -r2e3463e873227c6a3edcb3e02d55270219e553ff -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/quartz/org/quartz/xml/ValidationException.java (.../ValidationException.java) (revision 2e3463e873227c6a3edcb3e02d55270219e553ff) +++ 3rdParty_sources/quartz/org/quartz/xml/ValidationException.java (.../ValidationException.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005 OpenSymphony + * Copyright 2001-2009 Terracotta, Inc. * * 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 @@ -15,9 +15,6 @@ * */ -/* - * Previously Copyright (c) 2001-2004 James House - */ package org.quartz.xml; import java.util.ArrayList; @@ -26,11 +23,14 @@ import java.util.Iterator; /** - * Reports QuartzMetaDataProcessor validation exceptions. + * Reports JobSchedulingDataLoader validation exceptions. * * @author Chris Bonham */ public class ValidationException extends Exception { + + private static final long serialVersionUID = -1697832087051681357L; + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -39,7 +39,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - private Collection validationExceptions = new ArrayList(); + private Collection validationExceptions = new ArrayList(); /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -69,15 +69,32 @@ /** * Constructor for ValidationException. * - * @param validationExceptions + * @param errors * collection of validation exceptions. */ - public ValidationException(Collection errors) { + public ValidationException(Collection errors) { this(); this.validationExceptions = Collections .unmodifiableCollection(validationExceptions); + initCause(errors.iterator().next()); } + + /** + * Constructor for ValidationException. + * + * @param message + * exception message. + * @param errors + * collection of validation exceptions. + */ + public ValidationException(String message, Collection errors) { + this(message); + this.validationExceptions = Collections + .unmodifiableCollection(validationExceptions); + initCause(errors.iterator().next()); + } + /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * @@ -91,7 +108,7 @@ * * @return collection of errors. */ - public Collection getValidationExceptions() { + public Collection getValidationExceptions() { return validationExceptions; } @@ -100,16 +117,17 @@ * * @return the detail message string. */ + @Override public String getMessage() { if (getValidationExceptions().size() == 0) { return super.getMessage(); } StringBuffer sb = new StringBuffer(); boolean first = true; - for (Iterator iter = getValidationExceptions().iterator(); iter + for (Iterator iter = getValidationExceptions().iterator(); iter .hasNext(); ) { - Exception e = (Exception) iter.next(); + Exception e = iter.next(); if (!first) { sb.append('\n'); @@ -121,4 +139,6 @@ return sb.toString(); } + + } Index: 3rdParty_sources/quartz/org/quartz/xml/XMLSchedulingDataProcessor.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/xml/XMLSchedulingDataProcessor.java (revision 0) +++ 3rdParty_sources/quartz/org/quartz/xml/XMLSchedulingDataProcessor.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,1245 @@ +/* + * Copyright 2001-2010 Terracotta, Inc. + * + * 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 org.quartz.xml; + +import static org.quartz.CalendarIntervalScheduleBuilder.calendarIntervalSchedule; +import static org.quartz.CronScheduleBuilder.cronSchedule; +import static org.quartz.JobBuilder.newJob; +import static org.quartz.SimpleScheduleBuilder.simpleSchedule; +import static org.quartz.TriggerBuilder.newTrigger; +import static org.quartz.TriggerKey.triggerKey; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLDecoder; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import javax.xml.XMLConstants; +import javax.xml.namespace.NamespaceContext; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathException; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.quartz.*; +import org.quartz.DateBuilder.IntervalUnit; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.spi.ClassLoadHelper; +import org.quartz.spi.MutableTrigger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import javax.xml.bind.DatatypeConverter; + + +/** + * Parses an XML file that declares Jobs and their schedules (Triggers), and processes the related data. + * + * The xml document must conform to the format defined in + * "job_scheduling_data_2_0.xsd" + * + * The same instance can be used again and again, however a single instance is not thread-safe. + * + * @author James House + * @author Past contributions from Chris Bonham + * @author Past contributions from pl47ypus + * + * @since Quartz 1.8 + */ +public class XMLSchedulingDataProcessor implements ErrorHandler { + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constants. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + public static final String QUARTZ_NS = "http://www.quartz-scheduler.org/xml/JobSchedulingData"; + + public static final String QUARTZ_SCHEMA_WEB_URL = "http://www.quartz-scheduler.org/xml/job_scheduling_data_2_0.xsd"; + + public static final String QUARTZ_XSD_PATH_IN_JAR = "org/quartz/xml/job_scheduling_data_2_0.xsd"; + + public static final String QUARTZ_XML_DEFAULT_FILE_NAME = "quartz_data.xml"; + + public static final String QUARTZ_SYSTEM_ID_JAR_PREFIX = "jar:"; + + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Data members. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + // pre-processing commands + protected List jobGroupsToDelete = new LinkedList(); + protected List triggerGroupsToDelete = new LinkedList(); + protected List jobsToDelete = new LinkedList(); + protected List triggersToDelete = new LinkedList(); + + // scheduling commands + protected List loadedJobs = new LinkedList(); + protected List loadedTriggers = new LinkedList(); + + // directives + private boolean overWriteExistingData = true; + private boolean ignoreDuplicates = false; + + protected Collection validationExceptions = new ArrayList(); + + + protected ClassLoadHelper classLoadHelper; + protected List jobGroupsToNeverDelete = new LinkedList(); + protected List triggerGroupsToNeverDelete = new LinkedList(); + + private DocumentBuilder docBuilder = null; + private XPath xpath = null; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Constructors. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Constructor for JobSchedulingDataLoader. + * + * @param clh class-loader helper to share with digester. + * @throws ParserConfigurationException if the XML parser cannot be configured as needed. + */ + public XMLSchedulingDataProcessor(ClassLoadHelper clh) throws ParserConfigurationException { + this.classLoadHelper = clh; + initDocumentParser(); + } + + /** + * Initializes the XML parser. + * @throws ParserConfigurationException + */ + protected void initDocumentParser() throws ParserConfigurationException { + + DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); + + docBuilderFactory.setNamespaceAware(true); + docBuilderFactory.setValidating(true); + + docBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); + + docBuilderFactory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", resolveSchemaSource()); + + docBuilder = docBuilderFactory.newDocumentBuilder(); + + docBuilder.setErrorHandler(this); + + NamespaceContext nsContext = new NamespaceContext() + { + public String getNamespaceURI(String prefix) + { + if (prefix == null) + throw new IllegalArgumentException("Null prefix"); + if (XMLConstants.XML_NS_PREFIX.equals(prefix)) + return XMLConstants.XML_NS_URI; + if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) + return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; + + if ("q".equals(prefix)) + return QUARTZ_NS; + + return XMLConstants.NULL_NS_URI; + } + + public Iterator getPrefixes(String namespaceURI) + { + // This method isn't necessary for XPath processing. + throw new UnsupportedOperationException(); + } + + public String getPrefix(String namespaceURI) + { + // This method isn't necessary for XPath processing. + throw new UnsupportedOperationException(); + } + + }; + + xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(nsContext); + } + + protected Object resolveSchemaSource() { + InputSource inputSource; + + InputStream is = null; + + try { + is = classLoadHelper.getResourceAsStream(QUARTZ_XSD_PATH_IN_JAR); + } finally { + if (is != null) { + inputSource = new InputSource(is); + inputSource.setSystemId(QUARTZ_SCHEMA_WEB_URL); + log.debug("Utilizing schema packaged in local quartz distribution jar."); + } + else { + log.info("Unable to load local schema packaged in quartz distribution jar. Utilizing schema online at " + QUARTZ_SCHEMA_WEB_URL); + return QUARTZ_SCHEMA_WEB_URL; + } + + } + + return inputSource; + } + + /** + * Whether the existing scheduling data (with same identifiers) will be + * overwritten. + * + * If false, and IgnoreDuplicates is not false, and jobs or + * triggers with the same names already exist as those in the file, an + * error will occur. + * + * @see #isIgnoreDuplicates() + */ + public boolean isOverWriteExistingData() { + return overWriteExistingData; + } + + /** + * Whether the existing scheduling data (with same identifiers) will be + * overwritten. + * + * If false, and IgnoreDuplicates is not false, and jobs or + * triggers with the same names already exist as those in the file, an + * error will occur. + * + * @see #setIgnoreDuplicates(boolean) + */ + protected void setOverWriteExistingData(boolean overWriteExistingData) { + this.overWriteExistingData = overWriteExistingData; + } + + /** + * If true (and OverWriteExistingData is false) then any + * job/triggers encountered in this file that have names that already exist + * in the scheduler will be ignored, and no error will be produced. + * + * @see #isOverWriteExistingData() + */ + public boolean isIgnoreDuplicates() { + return ignoreDuplicates; + } + + /** + * If true (and OverWriteExistingData is false) then any + * job/triggers encountered in this file that have names that already exist + * in the scheduler will be ignored, and no error will be produced. + * + * @see #setOverWriteExistingData(boolean) + */ + public void setIgnoreDuplicates(boolean ignoreDuplicates) { + this.ignoreDuplicates = ignoreDuplicates; + } + + /** + * Add the given group to the list of job groups that will never be + * deleted by this processor, even if a pre-processing-command to + * delete the group is encountered. + */ + public void addJobGroupToNeverDelete(String group) { + if(group != null) + jobGroupsToNeverDelete.add(group); + } + + /** + * Remove the given group to the list of job groups that will never be + * deleted by this processor, even if a pre-processing-command to + * delete the group is encountered. + */ + public boolean removeJobGroupToNeverDelete(String group) { + return group != null && jobGroupsToNeverDelete.remove(group); + } + + /** + * Get the (unmodifiable) list of job groups that will never be + * deleted by this processor, even if a pre-processing-command to + * delete the group is encountered. + */ + public List getJobGroupsToNeverDelete() { + return Collections.unmodifiableList(jobGroupsToDelete); + } + + /** + * Add the given group to the list of trigger groups that will never be + * deleted by this processor, even if a pre-processing-command to + * delete the group is encountered. + */ + public void addTriggerGroupToNeverDelete(String group) { + if(group != null) + triggerGroupsToNeverDelete.add(group); + } + + /** + * Remove the given group to the list of trigger groups that will never be + * deleted by this processor, even if a pre-processing-command to + * delete the group is encountered. + */ + public boolean removeTriggerGroupToNeverDelete(String group) { + if(group != null) + return triggerGroupsToNeverDelete.remove(group); + return false; + } + + /** + * Get the (unmodifiable) list of trigger groups that will never be + * deleted by this processor, even if a pre-processing-command to + * delete the group is encountered. + */ + public List getTriggerGroupsToNeverDelete() { + return Collections.unmodifiableList(triggerGroupsToDelete); + } + + /* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Interface. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + + /** + * Process the xml file in the default location (a file named + * "quartz_jobs.xml" in the current working directory). + * + */ + protected void processFile() throws Exception { + processFile(QUARTZ_XML_DEFAULT_FILE_NAME); + } + + /** + * Process the xml file named fileName. + * + * @param fileName + * meta data file name. + */ + protected void processFile(String fileName) throws Exception { + processFile(fileName, getSystemIdForFileName(fileName)); + } + + /** + * For the given fileName, attempt to expand it to its full path + * for use as a system id. + * + * @see #getURL(String) + * @see #processFile() + * @see #processFile(String) + * @see #processFileAndScheduleJobs(Scheduler, boolean) + * @see #processFileAndScheduleJobs(String, org.quartz.Scheduler) + */ + protected String getSystemIdForFileName(String fileName) { + InputStream fileInputStream = null; + try { + String urlPath = null; + + File file = new File(fileName); // files in filesystem + if (!file.exists()) { + URL url = getURL(fileName); + if (url != null) { + try { + urlPath = URLDecoder.decode(url.getPath(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + log.warn("Unable to decode file path URL", e); + } + try { + fileInputStream = url.openStream(); + } catch (IOException ignore) { + } + } + } else { + try { + fileInputStream = new FileInputStream(file); + }catch (FileNotFoundException ignore) { + } + } + + if (fileInputStream == null) { + log.debug("Unable to resolve '" + fileName + "' to full path, so using it as is for system id."); + return fileName; + } else { + return (urlPath != null) ? urlPath : file.getAbsolutePath(); + } + } finally { + try { + if (fileInputStream != null) { + fileInputStream.close(); + } + } catch (IOException ioe) { + log.warn("Error closing jobs file: " + fileName, ioe); + } + } + } + + /** + * Returns an URL from the fileName as a resource. + * + * @param fileName + * file name. + * @return an URL from the fileName as a resource. + */ + protected URL getURL(String fileName) { + return classLoadHelper.getResource(fileName); + } + + protected void prepForProcessing() + { + clearValidationExceptions(); + + setOverWriteExistingData(true); + setIgnoreDuplicates(false); + + jobGroupsToDelete.clear(); + jobsToDelete.clear(); + triggerGroupsToDelete.clear(); + triggersToDelete.clear(); + + loadedJobs.clear(); + loadedTriggers.clear(); + } + + /** + * Process the xmlfile named fileName with the given system + * ID. + * + * @param fileName + * meta data file name. + * @param systemId + * system ID. + */ + protected void processFile(String fileName, String systemId) + throws ValidationException, ParserConfigurationException, + SAXException, IOException, SchedulerException, + ClassNotFoundException, ParseException, XPathException { + + prepForProcessing(); + + log.info("Parsing XML file: " + fileName + + " with systemId: " + systemId); + InputSource is = new InputSource(getInputStream(fileName)); + is.setSystemId(systemId); + + process(is); + + maybeThrowValidationException(); + } + + /** + * Process the xmlfile named fileName with the given system + * ID. + * + * @param stream + * an input stream containing the xml content. + * @param systemId + * system ID. + */ + public void processStreamAndScheduleJobs(InputStream stream, String systemId, Scheduler sched) + throws ValidationException, ParserConfigurationException, + SAXException, XPathException, IOException, SchedulerException, + ClassNotFoundException, ParseException { + + prepForProcessing(); + + log.info("Parsing XML from stream with systemId: " + systemId); + + InputSource is = new InputSource(stream); + is.setSystemId(systemId); + + process(is); + executePreProcessCommands(sched); + scheduleJobs(sched); + + maybeThrowValidationException(); + } + + @SuppressWarnings("ConstantConditions") + protected void process(InputSource is) throws SAXException, IOException, ParseException, XPathException, ClassNotFoundException { + + // load the document + Document document = docBuilder.parse(is); + + // + // Extract pre-processing commands + // + + NodeList deleteJobGroupNodes = (NodeList) xpath.evaluate( + "/q:job-scheduling-data/q:pre-processing-commands/q:delete-jobs-in-group", + document, XPathConstants.NODESET); + + log.debug("Found " + deleteJobGroupNodes.getLength() + " delete job group commands."); + + for (int i = 0; i < deleteJobGroupNodes.getLength(); i++) { + Node node = deleteJobGroupNodes.item(i); + String t = node.getTextContent(); + if(t == null || (t = t.trim()).length() == 0) + continue; + jobGroupsToDelete.add(t); + } + + NodeList deleteTriggerGroupNodes = (NodeList) xpath.evaluate( + "/q:job-scheduling-data/q:pre-processing-commands/q:delete-triggers-in-group", + document, XPathConstants.NODESET); + + log.debug("Found " + deleteTriggerGroupNodes.getLength() + " delete trigger group commands."); + + for (int i = 0; i < deleteTriggerGroupNodes.getLength(); i++) { + Node node = deleteTriggerGroupNodes.item(i); + String t = node.getTextContent(); + if(t == null || (t = t.trim()).length() == 0) + continue; + triggerGroupsToDelete.add(t); + } + + NodeList deleteJobNodes = (NodeList) xpath.evaluate( + "/q:job-scheduling-data/q:pre-processing-commands/q:delete-job", + document, XPathConstants.NODESET); + + log.debug("Found " + deleteJobNodes.getLength() + " delete job commands."); + + for (int i = 0; i < deleteJobNodes.getLength(); i++) { + Node node = deleteJobNodes.item(i); + + String name = getTrimmedToNullString(xpath, "q:name", node); + String group = getTrimmedToNullString(xpath, "q:group", node); + + if(name == null) + throw new ParseException("Encountered a 'delete-job' command without a name specified.", -1); + jobsToDelete.add(new JobKey(name, group)); + } + + NodeList deleteTriggerNodes = (NodeList) xpath.evaluate( + "/q:job-scheduling-data/q:pre-processing-commands/q:delete-trigger", + document, XPathConstants.NODESET); + + log.debug("Found " + deleteTriggerNodes.getLength() + " delete trigger commands."); + + for (int i = 0; i < deleteTriggerNodes.getLength(); i++) { + Node node = deleteTriggerNodes.item(i); + + String name = getTrimmedToNullString(xpath, "q:name", node); + String group = getTrimmedToNullString(xpath, "q:group", node); + + if(name == null) + throw new ParseException("Encountered a 'delete-trigger' command without a name specified.", -1); + triggersToDelete.add(new TriggerKey(name, group)); + } + + // + // Extract directives + // + + Boolean overWrite = getBoolean(xpath, + "/q:job-scheduling-data/q:processing-directives/q:overwrite-existing-data", document); + if(overWrite == null) { + log.debug("Directive 'overwrite-existing-data' not specified, defaulting to " + isOverWriteExistingData()); + } + else { + log.debug("Directive 'overwrite-existing-data' specified as: " + overWrite); + setOverWriteExistingData(overWrite); + } + + Boolean ignoreDupes = getBoolean(xpath, + "/q:job-scheduling-data/q:processing-directives/q:ignore-duplicates", document); + if(ignoreDupes == null) { + log.debug("Directive 'ignore-duplicates' not specified, defaulting to " + isIgnoreDuplicates()); + } + else { + log.debug("Directive 'ignore-duplicates' specified as: " + ignoreDupes); + setIgnoreDuplicates(ignoreDupes); + } + + // + // Extract Job definitions... + // + + NodeList jobNodes = (NodeList) xpath.evaluate("/q:job-scheduling-data/q:schedule/q:job", + document, XPathConstants.NODESET); + + log.debug("Found " + jobNodes.getLength() + " job definitions."); + + for (int i = 0; i < jobNodes.getLength(); i++) { + Node jobDetailNode = jobNodes.item(i); + String t = null; + + String jobName = getTrimmedToNullString(xpath, "q:name", jobDetailNode); + String jobGroup = getTrimmedToNullString(xpath, "q:group", jobDetailNode); + String jobDescription = getTrimmedToNullString(xpath, "q:description", jobDetailNode); + String jobClassName = getTrimmedToNullString(xpath, "q:job-class", jobDetailNode); + t = getTrimmedToNullString(xpath, "q:durability", jobDetailNode); + boolean jobDurability = (t != null) && t.equals("true"); + t = getTrimmedToNullString(xpath, "q:recover", jobDetailNode); + boolean jobRecoveryRequested = (t != null) && t.equals("true"); + + Class jobClass = classLoadHelper.loadClass(jobClassName, Job.class); + + JobDetail jobDetail = newJob(jobClass) + .withIdentity(jobName, jobGroup) + .withDescription(jobDescription) + .storeDurably(jobDurability) + .requestRecovery(jobRecoveryRequested) + .build(); + + NodeList jobDataEntries = (NodeList) xpath.evaluate( + "q:job-data-map/q:entry", jobDetailNode, + XPathConstants.NODESET); + + for (int k = 0; k < jobDataEntries.getLength(); k++) { + Node entryNode = jobDataEntries.item(k); + String key = getTrimmedToNullString(xpath, "q:key", entryNode); + String value = getTrimmedToNullString(xpath, "q:value", entryNode); + jobDetail.getJobDataMap().put(key, value); + } + + if(log.isDebugEnabled()) + log.debug("Parsed job definition: " + jobDetail); + + addJobToSchedule(jobDetail); + } + + // + // Extract Trigger definitions... + // + + NodeList triggerEntries = (NodeList) xpath.evaluate( + "/q:job-scheduling-data/q:schedule/q:trigger/*", document, XPathConstants.NODESET); + + log.debug("Found " + triggerEntries.getLength() + " trigger definitions."); + + for (int j = 0; j < triggerEntries.getLength(); j++) { + Node triggerNode = triggerEntries.item(j); + String triggerName = getTrimmedToNullString(xpath, "q:name", triggerNode); + String triggerGroup = getTrimmedToNullString(xpath, "q:group", triggerNode); + String triggerDescription = getTrimmedToNullString(xpath, "q:description", triggerNode); + String triggerMisfireInstructionConst = getTrimmedToNullString(xpath, "q:misfire-instruction", triggerNode); + String triggerPriorityString = getTrimmedToNullString(xpath, "q:priority", triggerNode); + String triggerCalendarRef = getTrimmedToNullString(xpath, "q:calendar-name", triggerNode); + String triggerJobName = getTrimmedToNullString(xpath, "q:job-name", triggerNode); + String triggerJobGroup = getTrimmedToNullString(xpath, "q:job-group", triggerNode); + + int triggerPriority = Trigger.DEFAULT_PRIORITY; + if(triggerPriorityString != null) + triggerPriority = Integer.valueOf(triggerPriorityString); + + String startTimeString = getTrimmedToNullString(xpath, "q:start-time", triggerNode); + String startTimeFutureSecsString = getTrimmedToNullString(xpath, "q:start-time-seconds-in-future", triggerNode); + String endTimeString = getTrimmedToNullString(xpath, "q:end-time", triggerNode); + + //QTZ-273 : use of DatatypeConverter.parseDateTime() instead of SimpleDateFormat + Date triggerStartTime; + if(startTimeFutureSecsString != null) + triggerStartTime = new Date(System.currentTimeMillis() + (Long.valueOf(startTimeFutureSecsString) * 1000L)); + else + triggerStartTime = (startTimeString == null || startTimeString.length() == 0 ? new Date() : DatatypeConverter.parseDateTime(startTimeString).getTime()); + Date triggerEndTime = endTimeString == null || endTimeString.length() == 0 ? null : DatatypeConverter.parseDateTime(endTimeString).getTime(); + + TriggerKey triggerKey = triggerKey(triggerName, triggerGroup); + + ScheduleBuilder sched; + + if (triggerNode.getNodeName().equals("simple")) { + String repeatCountString = getTrimmedToNullString(xpath, "q:repeat-count", triggerNode); + String repeatIntervalString = getTrimmedToNullString(xpath, "q:repeat-interval", triggerNode); + + int repeatCount = repeatCountString == null ? 0 : Integer.parseInt(repeatCountString); + long repeatInterval = repeatIntervalString == null ? 0 : Long.parseLong(repeatIntervalString); + + sched = simpleSchedule() + .withIntervalInMilliseconds(repeatInterval) + .withRepeatCount(repeatCount); + + if (triggerMisfireInstructionConst != null && triggerMisfireInstructionConst.length() != 0) { + if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_FIRE_NOW")) + ((SimpleScheduleBuilder)sched).withMisfireHandlingInstructionFireNow(); + else if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT")) + ((SimpleScheduleBuilder)sched).withMisfireHandlingInstructionNextWithExistingCount(); + else if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT")) + ((SimpleScheduleBuilder)sched).withMisfireHandlingInstructionNextWithRemainingCount(); + else if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT")) + ((SimpleScheduleBuilder)sched).withMisfireHandlingInstructionNowWithExistingCount(); + else if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT")) + ((SimpleScheduleBuilder)sched).withMisfireHandlingInstructionNowWithRemainingCount(); + else if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_SMART_POLICY")) { + // do nothing.... (smart policy is default) + } + else + throw new ParseException("Unexpected/Unhandlable Misfire Instruction encountered '" + triggerMisfireInstructionConst + "', for trigger: " + triggerKey, -1); + } + } else if (triggerNode.getNodeName().equals("cron")) { + String cronExpression = getTrimmedToNullString(xpath, "q:cron-expression", triggerNode); + String timezoneString = getTrimmedToNullString(xpath, "q:time-zone", triggerNode); + + TimeZone tz = timezoneString == null ? null : TimeZone.getTimeZone(timezoneString); + + sched = cronSchedule(cronExpression) + .inTimeZone(tz); + + if (triggerMisfireInstructionConst != null && triggerMisfireInstructionConst.length() != 0) { + if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_DO_NOTHING")) + ((CronScheduleBuilder)sched).withMisfireHandlingInstructionDoNothing(); + else if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_FIRE_ONCE_NOW")) + ((CronScheduleBuilder)sched).withMisfireHandlingInstructionFireAndProceed(); + else if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_SMART_POLICY")) { + // do nothing.... (smart policy is default) + } + else + throw new ParseException("Unexpected/Unhandlable Misfire Instruction encountered '" + triggerMisfireInstructionConst + "', for trigger: " + triggerKey, -1); + } + } else if (triggerNode.getNodeName().equals("calendar-interval")) { + String repeatIntervalString = getTrimmedToNullString(xpath, "q:repeat-interval", triggerNode); + String repeatUnitString = getTrimmedToNullString(xpath, "q:repeat-interval-unit", triggerNode); + + int repeatInterval = Integer.parseInt(repeatIntervalString); + + IntervalUnit repeatUnit = IntervalUnit.valueOf(repeatUnitString); + + sched = calendarIntervalSchedule() + .withInterval(repeatInterval, repeatUnit); + + if (triggerMisfireInstructionConst != null && triggerMisfireInstructionConst.length() != 0) { + if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_DO_NOTHING")) + ((CalendarIntervalScheduleBuilder)sched).withMisfireHandlingInstructionDoNothing(); + else if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_FIRE_ONCE_NOW")) + ((CalendarIntervalScheduleBuilder)sched).withMisfireHandlingInstructionFireAndProceed(); + else if(triggerMisfireInstructionConst.equals("MISFIRE_INSTRUCTION_SMART_POLICY")) { + // do nothing.... (smart policy is default) + } + else + throw new ParseException("Unexpected/Unhandlable Misfire Instruction encountered '" + triggerMisfireInstructionConst + "', for trigger: " + triggerKey, -1); + } + } else { + throw new ParseException("Unknown trigger type: " + triggerNode.getNodeName(), -1); + } + + + MutableTrigger trigger = (MutableTrigger) newTrigger() + .withIdentity(triggerName, triggerGroup) + .withDescription(triggerDescription) + .forJob(triggerJobName, triggerJobGroup) + .startAt(triggerStartTime) + .endAt(triggerEndTime) + .withPriority(triggerPriority) + .modifiedByCalendar(triggerCalendarRef) + .withSchedule(sched) + .build(); + + NodeList jobDataEntries = (NodeList) xpath.evaluate( + "q:job-data-map/q:entry", triggerNode, + XPathConstants.NODESET); + + for (int k = 0; k < jobDataEntries.getLength(); k++) { + Node entryNode = jobDataEntries.item(k); + String key = getTrimmedToNullString(xpath, "q:key", entryNode); + String value = getTrimmedToNullString(xpath, "q:value", entryNode); + trigger.getJobDataMap().put(key, value); + } + + if(log.isDebugEnabled()) + log.debug("Parsed trigger definition: " + trigger); + + addTriggerToSchedule(trigger); + } + } + + protected String getTrimmedToNullString(XPath xpathToElement, String elementName, Node parentNode) throws XPathExpressionException { + String str = (String) xpathToElement.evaluate(elementName, + parentNode, XPathConstants.STRING); + + if(str != null) + str = str.trim(); + + if(str != null && str.length() == 0) + str = null; + + return str; + } + + protected Boolean getBoolean(XPath xpathToElement, String elementName, Document document) throws XPathExpressionException { + + Node directive = (Node) xpathToElement.evaluate(elementName, document, XPathConstants.NODE); + + if(directive == null || directive.getTextContent() == null) + return null; + + String val = directive.getTextContent(); + if(val.equalsIgnoreCase("true") || val.equalsIgnoreCase("yes") || val.equalsIgnoreCase("y")) + return Boolean.TRUE; + + return Boolean.FALSE; + } + + /** + * Process the xml file in the default location, and schedule all of the + * jobs defined within it. + * + *

Note that we will set overWriteExistingJobs after the default xml is parsed. + */ + public void processFileAndScheduleJobs(Scheduler sched, + boolean overWriteExistingJobs) throws Exception { + String fileName = QUARTZ_XML_DEFAULT_FILE_NAME; + processFile(fileName, getSystemIdForFileName(fileName)); + // The overWriteExistingJobs flag was set by processFile() -> prepForProcessing(), then by xml parsing, and then now + // we need to reset it again here by this method parameter to override it. + setOverWriteExistingData(overWriteExistingJobs); + executePreProcessCommands(sched); + scheduleJobs(sched); + } + + /** + * Process the xml file in the given location, and schedule all of the + * jobs defined within it. + * + * @param fileName + * meta data file name. + */ + public void processFileAndScheduleJobs(String fileName, Scheduler sched) throws Exception { + processFileAndScheduleJobs(fileName, getSystemIdForFileName(fileName), sched); + } + + /** + * Process the xml file in the given location, and schedule all of the + * jobs defined within it. + * + * @param fileName + * meta data file name. + */ + public void processFileAndScheduleJobs(String fileName, String systemId, Scheduler sched) throws Exception { + processFile(fileName, systemId); + executePreProcessCommands(sched); + scheduleJobs(sched); + } + + /** + * Returns a List of jobs loaded from the xml file. + *

+ * + * @return a List of jobs. + */ + protected List getLoadedJobs() { + return Collections.unmodifiableList(loadedJobs); + } + + /** + * Returns a List of triggers loaded from the xml file. + *

+ * + * @return a List of triggers. + */ + protected List getLoadedTriggers() { + return Collections.unmodifiableList(loadedTriggers); + } + + /** + * Returns an InputStream from the fileName as a resource. + * + * @param fileName + * file name. + * @return an InputStream from the fileName as a resource. + */ + protected InputStream getInputStream(String fileName) { + return this.classLoadHelper.getResourceAsStream(fileName); + } + + protected void addJobToSchedule(JobDetail job) { + loadedJobs.add(job); + } + + protected void addTriggerToSchedule(MutableTrigger trigger) { + loadedTriggers.add(trigger); + } + + private Map> buildTriggersByFQJobNameMap(List triggers) { + + Map> triggersByFQJobName = new HashMap>(); + + for(MutableTrigger trigger: triggers) { + List triggersOfJob = triggersByFQJobName.get(trigger.getJobKey()); + if(triggersOfJob == null) { + triggersOfJob = new LinkedList(); + triggersByFQJobName.put(trigger.getJobKey(), triggersOfJob); + } + triggersOfJob.add(trigger); + } + + return triggersByFQJobName; + } + + protected void executePreProcessCommands(Scheduler scheduler) + throws SchedulerException { + + for(String group: jobGroupsToDelete) { + if(group.equals("*")) { + log.info("Deleting all jobs in ALL groups."); + for (String groupName : scheduler.getJobGroupNames()) { + if (!jobGroupsToNeverDelete.contains(groupName)) { + for (JobKey key : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) { + scheduler.deleteJob(key); + } + } + } + } + else { + if(!jobGroupsToNeverDelete.contains(group)) { + log.info("Deleting all jobs in group: {}", group); + for (JobKey key : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(group))) { + scheduler.deleteJob(key); + } + } + } + } + + for(String group: triggerGroupsToDelete) { + if(group.equals("*")) { + log.info("Deleting all triggers in ALL groups."); + for (String groupName : scheduler.getTriggerGroupNames()) { + if (!triggerGroupsToNeverDelete.contains(groupName)) { + for (TriggerKey key : scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(groupName))) { + scheduler.unscheduleJob(key); + } + } + } + } + else { + if(!triggerGroupsToNeverDelete.contains(group)) { + log.info("Deleting all triggers in group: {}", group); + for (TriggerKey key : scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(group))) { + scheduler.unscheduleJob(key); + } + } + } + } + + for(JobKey key: jobsToDelete) { + if(!jobGroupsToNeverDelete.contains(key.getGroup())) { + log.info("Deleting job: {}", key); + scheduler.deleteJob(key); + } + } + + for(TriggerKey key: triggersToDelete) { + if(!triggerGroupsToNeverDelete.contains(key.getGroup())) { + log.info("Deleting trigger: {}", key); + scheduler.unscheduleJob(key); + } + } + } + + /** + * Schedules the given sets of jobs and triggers. + * + * @param sched + * job scheduler. + * @exception SchedulerException + * if the Job or Trigger cannot be added to the Scheduler, or + * there is an internal Scheduler error. + */ + @SuppressWarnings("ConstantConditions") + protected void scheduleJobs(Scheduler sched) + throws SchedulerException { + + List jobs = new LinkedList(getLoadedJobs()); + List triggers = new LinkedList( getLoadedTriggers()); + + log.info("Adding " + jobs.size() + " jobs, " + triggers.size() + " triggers."); + + Map> triggersByFQJobName = buildTriggersByFQJobNameMap(triggers); + + // add each job, and it's associated triggers + Iterator itr = jobs.iterator(); + while(itr.hasNext()) { + JobDetail detail = itr.next(); + itr.remove(); // remove jobs as we handle them... + + JobDetail dupeJ = null; + try { + // The existing job could have been deleted, and Quartz API doesn't allow us to query this without + // loading the job class, so use try/catch to handle it. + dupeJ = sched.getJobDetail(detail.getKey()); + } catch (JobPersistenceException e) { + if (e.getCause() instanceof ClassNotFoundException && isOverWriteExistingData()) { + // We are going to replace jobDetail anyway, so just delete it first. + log.info("Removing job: " + detail.getKey()); + sched.deleteJob(detail.getKey()); + } else { + throw e; + } + } + + if ((dupeJ != null)) { + if(!isOverWriteExistingData() && isIgnoreDuplicates()) { + log.info("Not overwriting existing job: " + dupeJ.getKey()); + continue; // just ignore the entry + } + if(!isOverWriteExistingData() && !isIgnoreDuplicates()) { + throw new ObjectAlreadyExistsException(detail); + } + } + + if (dupeJ != null) { + log.info("Replacing job: " + detail.getKey()); + } else { + log.info("Adding job: " + detail.getKey()); + } + + List triggersOfJob = triggersByFQJobName.get(detail.getKey()); + + if (!detail.isDurable() && (triggersOfJob == null || triggersOfJob.size() == 0)) { + if (dupeJ == null) { + throw new SchedulerException( + "A new job defined without any triggers must be durable: " + + detail.getKey()); + } + + if ((dupeJ.isDurable() && + (sched.getTriggersOfJob( + detail.getKey()).size() == 0))) { + throw new SchedulerException( + "Can't change existing durable job without triggers to non-durable: " + + detail.getKey()); + } + } + + + if(dupeJ != null || detail.isDurable()) { + if (triggersOfJob != null && triggersOfJob.size() > 0) + sched.addJob(detail, true, true); // add the job regardless is durable or not b/c we have trigger to add + else + sched.addJob(detail, true, false); // add the job only if a replacement or durable, else exception will throw! + } + else { + boolean addJobWithFirstSchedule = true; + + // Add triggers related to the job... + for (MutableTrigger trigger : triggersOfJob) { + triggers.remove(trigger); // remove triggers as we handle them... + + if (trigger.getStartTime() == null) { + trigger.setStartTime(new Date()); + } + + Trigger dupeT = sched.getTrigger(trigger.getKey()); + if (dupeT != null) { + if (isOverWriteExistingData()) { + if (log.isDebugEnabled()) { + log.debug( + "Rescheduling job: " + trigger.getJobKey() + " with updated trigger: " + trigger.getKey()); + } + } else if (isIgnoreDuplicates()) { + log.info("Not overwriting existing trigger: " + dupeT.getKey()); + continue; // just ignore the trigger (and possibly job) + } else { + throw new ObjectAlreadyExistsException(trigger); + } + + if (!dupeT.getJobKey().equals(trigger.getJobKey())) { + log.warn("Possibly duplicately named ({}) triggers in jobs xml file! ", trigger.getKey()); + } + + sched.rescheduleJob(trigger.getKey(), trigger); + } else { + if (log.isDebugEnabled()) { + log.debug( + "Scheduling job: " + trigger.getJobKey() + " with trigger: " + trigger.getKey()); + } + + try { + if (addJobWithFirstSchedule) { + sched.scheduleJob(detail, trigger); // add the job if it's not in yet... + addJobWithFirstSchedule = false; + } else { + sched.scheduleJob(trigger); + } + } catch (ObjectAlreadyExistsException e) { + if (log.isDebugEnabled()) { + log.debug( + "Adding trigger: " + trigger.getKey() + " for job: " + detail.getKey() + + " failed because the trigger already existed. " + + "This is likely due to a race condition between multiple instances " + + "in the cluster. Will try to reschedule instead."); + } + + // Let's try one more time as reschedule. + sched.rescheduleJob(trigger.getKey(), trigger); + } + } + } + } + } + + // add triggers that weren't associated with a new job... (those we already handled were removed above) + for(MutableTrigger trigger: triggers) { + + if(trigger.getStartTime() == null) { + trigger.setStartTime(new Date()); + } + + Trigger dupeT = sched.getTrigger(trigger.getKey()); + if (dupeT != null) { + if(isOverWriteExistingData()) { + if (log.isDebugEnabled()) { + log.debug( + "Rescheduling job: " + trigger.getJobKey() + " with updated trigger: " + trigger.getKey()); + } + } + else if(isIgnoreDuplicates()) { + log.info("Not overwriting existing trigger: " + dupeT.getKey()); + continue; // just ignore the trigger + } + else { + throw new ObjectAlreadyExistsException(trigger); + } + + if(!dupeT.getJobKey().equals(trigger.getJobKey())) { + log.warn("Possibly duplicately named ({}) triggers in jobs xml file! ", trigger.getKey()); + } + + sched.rescheduleJob(trigger.getKey(), trigger); + } else { + if (log.isDebugEnabled()) { + log.debug( + "Scheduling job: " + trigger.getJobKey() + " with trigger: " + trigger.getKey()); + } + + try { + sched.scheduleJob(trigger); + } catch (ObjectAlreadyExistsException e) { + if (log.isDebugEnabled()) { + log.debug( + "Adding trigger: " + trigger.getKey() + " for job: " +trigger.getJobKey() + + " failed because the trigger already existed. " + + "This is likely due to a race condition between multiple instances " + + "in the cluster. Will try to reschedule instead."); + } + + // Let's rescheduleJob one more time. + sched.rescheduleJob(trigger.getKey(), trigger); + } + } + } + } + + /** + * ErrorHandler interface. + * + * Receive notification of a warning. + * + * @param e + * The error information encapsulated in a SAX parse exception. + * @exception SAXException + * Any SAX exception, possibly wrapping another exception. + */ + public void warning(SAXParseException e) throws SAXException { + addValidationException(e); + } + + /** + * ErrorHandler interface. + * + * Receive notification of a recoverable error. + * + * @param e + * The error information encapsulated in a SAX parse exception. + * @exception SAXException + * Any SAX exception, possibly wrapping another exception. + */ + public void error(SAXParseException e) throws SAXException { + addValidationException(e); + } + + /** + * ErrorHandler interface. + * + * Receive notification of a non-recoverable error. + * + * @param e + * The error information encapsulated in a SAX parse exception. + * @exception SAXException + * Any SAX exception, possibly wrapping another exception. + */ + public void fatalError(SAXParseException e) throws SAXException { + addValidationException(e); + } + + /** + * Adds a detected validation exception. + * + * @param e + * SAX exception. + */ + protected void addValidationException(SAXException e) { + validationExceptions.add(e); + } + + /** + * Resets the the number of detected validation exceptions. + */ + protected void clearValidationExceptions() { + validationExceptions.clear(); + } + + /** + * Throws a ValidationException if the number of validationExceptions + * detected is greater than zero. + * + * @exception ValidationException + * DTD validation exception. + */ + protected void maybeThrowValidationException() throws ValidationException { + if (validationExceptions.size() > 0) { + throw new ValidationException("Encountered " + validationExceptions.size() + " validation exceptions.", validationExceptions); + } + } +} Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/xml/job_scheduling_data_1_5.dtd'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag c208628989d52041b3765784f4c8cbfd6c80d47b refers to a dead (removed) revision in file `3rdParty_sources/quartz/org/quartz/xml/job_scheduling_data_1_5.xsd'. Fisheye: No comparison available. Pass `N' to diff? Index: 3rdParty_sources/quartz/org/quartz/xml/job_scheduling_data_1_8.xsd =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/xml/job_scheduling_data_1_8.xsd (revision 0) +++ 3rdParty_sources/quartz/org/quartz/xml/job_scheduling_data_1_8.xsd (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,339 @@ + + + + + + Root level node + + + + + + Commands to be executed before scheduling the jobs and triggers in this file. + + + + + Directives to be followed while scheduling the jobs and triggers in this file. + + + + + + + + + + + + + + Version of the XML Schema instance + + + + + + + + + Delete all jobs, if any, in the identified group. "*" can be used to identify all groups. Will also result in deleting all triggers related to the jobs. + + + Delete all triggers, if any, in the identified group. "*" can be used to identify all groups. Will also result in deletion of related jobs that are non-durable. + + + Delete the identified job if it exists (will also result in deleting all triggers related to it). + + + + + + + + + Delete the identified trigger if it exists (will also result in deletion of related jobs that are non-durable). + + + + + + + + + + + + + + Whether the existing scheduling data (with same identifiers) will be overwritten. If false, and ignore-duplicates is not false, and jobs or triggers with the same names already exist as those in the file, an error will occur. + + + If true (and overwrite-existing-data is false) then any job/triggers encountered in this file that have names that already exist in the scheduler will be ignored, and no error will be produced. + + + + + + + Define a JobDetail + + + + + + + + + + + + + + + + + + + Define a JobDataMap + + + + + + + + + Define a JobDataMap entry + + + + + + + + + + Define a Trigger + + + + + + + + + + + Common Trigger definitions + + + + + + + + + + + + + + + + + + + + Define a SimpleTrigger + + + + + + + + + + + + + + + + + Define a CronTrigger + + + + + + + + + + + + + + + Define a DateIntervalTrigger + + + + + + + + + + + + + + + + Cron expression (see JavaDoc for examples) + + Special thanks to Chris Thatcher (thatcher@butterfly.net) for the regular expression! + + Regular expressions are not my strong point but I believe this is complete, + with the caveat that order for expressions like 3-0 is not legal but will pass, + and month and day names must be capitalized. + If you want to examine the correctness look for the [\s] to denote the + seperation of individual regular expressions. This is how I break them up visually + to examine them: + + SECONDS: + ( + ((([0-9]|[0-5][0-9]),)*([0-9]|[0-5][0-9])) + | (([\*]|[0-9]|[0-5][0-9])(/|-)([0-9]|[0-5][0-9])) + | ([\?]) + | ([\*]) + ) [\s] + MINUTES: + ( + ((([0-9]|[0-5][0-9]),)*([0-9]|[0-5][0-9])) + | (([\*]|[0-9]|[0-5][0-9])(/|-)([0-9]|[0-5][0-9])) + | ([\?]) + | ([\*]) + ) [\s] + HOURS: + ( + ((([0-9]|[0-1][0-9]|[2][0-3]),)*([0-9]|[0-1][0-9]|[2][0-3])) + | (([\*]|[0-9]|[0-1][0-9]|[2][0-3])(/|-)([0-9]|[0-1][0-9]|[2][0-3])) + | ([\?]) + | ([\*]) + ) [\s] + DAY OF MONTH: + ( + ((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]),)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?) + | (([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(/|-)([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?) + | (L) + | (LW) + | ([1-9]W) + | ([1-3][0-9]W) + | ([\?]) + | ([\*]) + )[\s] + MONTH: + ( + ((([1-9]|0[1-9]|1[0-2]),)*([1-9]|0[1-9]|1[0-2])) + | (([1-9]|0[1-9]|1[0-2])(/|-)([1-9]|0[1-9]|1[0-2])) + | (((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC),)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)) + | ((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-|/)(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)) + | ([\?]) + | ([\*]) + )[\s] + DAY OF WEEK: + ( + (([1-7],)*([1-7])) + | ([1-7](/|-)([1-7])) + | (((MON|TUE|WED|THU|FRI|SAT|SUN),)*(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?) + | ((MON|TUE|WED|THU|FRI|SAT|SUN)(-|/)(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?) + | (([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))(L|LW)?) + | (([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?) + | ([\?]) + | ([\*]) + ) + YEAR (OPTIONAL): + ( + [\s]? + ([\*])? + | ((19[7-9][0-9])|(20[0-9][0-9]))? + | (((19[7-9][0-9])|(20[0-9][0-9]))(-|/)((19[7-9][0-9])|(20[0-9][0-9])))? + | ((((19[7-9][0-9])|(20[0-9][0-9])),)*((19[7-9][0-9])|(20[0-9][0-9])))? + ) + + + + + + + + + + Number of times to repeat the Trigger (-1 for indefinite) + + + + + + + + + + Simple Trigger Misfire Instructions + + + + + + + + + + + + + + Simple Trigger Misfire Instructions + + + + + + + + + + + Date Interval Trigger Misfire Instructions + + + + + + + + + + + Interval Units + + + + + + + + + + + + + Index: 3rdParty_sources/quartz/org/quartz/xml/job_scheduling_data_2_0.xsd =================================================================== diff -u --- 3rdParty_sources/quartz/org/quartz/xml/job_scheduling_data_2_0.xsd (revision 0) +++ 3rdParty_sources/quartz/org/quartz/xml/job_scheduling_data_2_0.xsd (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,342 @@ + + + + + + Root level node + + + + + + Commands to be executed before scheduling the jobs and triggers in this file. + + + + + Directives to be followed while scheduling the jobs and triggers in this file. + + + + + + + + + + + + + + Version of the XML Schema instance + + + + + + + + + Delete all jobs, if any, in the identified group. "*" can be used to identify all groups. Will also result in deleting all triggers related to the jobs. + + + Delete all triggers, if any, in the identified group. "*" can be used to identify all groups. Will also result in deletion of related jobs that are non-durable. + + + Delete the identified job if it exists (will also result in deleting all triggers related to it). + + + + + + + + + Delete the identified trigger if it exists (will also result in deletion of related jobs that are non-durable). + + + + + + + + + + + + + + Whether the existing scheduling data (with same identifiers) will be overwritten. If false, and ignore-duplicates is not false, and jobs or triggers with the same names already exist as those in the file, an error will occur. + + + If true (and overwrite-existing-data is false) then any job/triggers encountered in this file that have names that already exist in the scheduler will be ignored, and no error will be produced. + + + + + + + Define a JobDetail + + + + + + + + + + + + + + + + + Define a JobDataMap + + + + + + + + + Define a JobDataMap entry + + + + + + + + + + Define a Trigger + + + + + + + + + + + Common Trigger definitions + + + + + + + + + + + + + + + + + + + + + + + Define a SimpleTrigger + + + + + + + + + + + + + + + + + Define a CronTrigger + + + + + + + + + + + + + + + Define a DateIntervalTrigger + + + + + + + + + + + + + + + + Cron expression (see JavaDoc for examples) + + Special thanks to Chris Thatcher (thatcher@butterfly.net) for the regular expression! + + Regular expressions are not my strong point but I believe this is complete, + with the caveat that order for expressions like 3-0 is not legal but will pass, + and month and day names must be capitalized. + If you want to examine the correctness look for the [\s] to denote the + seperation of individual regular expressions. This is how I break them up visually + to examine them: + + SECONDS: + ( + ((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?) + | (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9])) + | ([\?]) + | ([\*]) + ) [\s] + MINUTES: + ( + ((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?) + | (([\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9])) + | ([\?]) + | ([\*]) + ) [\s] + HOURS: + ( + ((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?) + | (([\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3])) + | ([\?]) + | ([\*]) + ) [\s] + DAY OF MONTH: + ( + ((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?) + | (([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?) + | (L(-[0-9])?) + | (L(-[1-2][0-9])?) + | (L(-[3][0-1])?) + | (LW) + | ([1-9]W) + | ([1-3][0-9]W) + | ([\?]) + | ([\*]) + )[\s] + MONTH: + ( + ((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?) + | (([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2])) + | (((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?) + | ((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)) + | ([\?]) + | ([\*]) + )[\s] + DAY OF WEEK: + ( + (([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?) + | ([1-7]/([1-7])) + | (((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?) + | ((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?) + | (([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))(L|LW)?) + | (([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?) + | ([\?]) + | ([\*]) + ) + YEAR (OPTIONAL): + ( + [\s]? + ([\*])? + | ((19[7-9][0-9])|(20[0-9][0-9]))? + | (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))? + | ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)? + ) + + + + + + + + + + Number of times to repeat the Trigger (-1 for indefinite) + + + + + + + + + + Simple Trigger Misfire Instructions + + + + + + + + + + + + + + Cron Trigger Misfire Instructions + + + + + + + + + + + Date Interval Trigger Misfire Instructions + + + + + + + + + + + Interval Units + + + + + + + + + + + + + Index: 3rdParty_sources/quartz/org/terracotta/quartz/AbstractTerracottaJobStore.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/AbstractTerracottaJobStore.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/AbstractTerracottaJobStore.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,604 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz; + +import org.quartz.Calendar; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.JobPersistenceException; +import org.quartz.ObjectAlreadyExistsException; +import org.quartz.SchedulerConfigException; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.quartz.impl.StdSchedulerFactory; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.spi.ClassLoadHelper; +import org.quartz.spi.JobStore; +import org.quartz.spi.OperableTrigger; +import org.quartz.spi.SchedulerSignaler; +import org.quartz.spi.TriggerFiredResult; +import org.terracotta.toolkit.internal.ToolkitInternal; +import org.terracotta.toolkit.rejoin.RejoinException; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * @author Alex Snaps + */ +public abstract class AbstractTerracottaJobStore implements JobStore { + public static final String TC_CONFIG_PROP = StdSchedulerFactory.PROP_JOB_STORE_PREFIX + + ".tcConfig"; + public static final String TC_CONFIGURL_PROP = StdSchedulerFactory.PROP_JOB_STORE_PREFIX + + ".tcConfigUrl"; + public static final String TC_REJOIN_PROP = StdSchedulerFactory.PROP_JOB_STORE_PREFIX + + ".rejoin"; + + private volatile ToolkitInternal toolkit; + private volatile TerracottaJobStoreExtensions realJobStore; + private String tcConfig = null; + private String tcConfigUrl = null; + private String schedInstId = null; + private String schedName = null; + private Long misFireThreshold = null; + private String synchWrite = null; + private String rejoin = null; + private Long estimatedTimeToReleaseAndAcquireTrigger = null; + private long tcRetryInterval = TimeUnit.SECONDS.toMillis(15); + + private void init() throws SchedulerConfigException { + if (realJobStore != null) { return; } + + if ((tcConfig != null) && (tcConfigUrl != null)) { + // + throw new SchedulerConfigException("Both " + TC_CONFIG_PROP + " and " + TC_CONFIGURL_PROP + + " are set in your properties. Please define only one of them"); + } + + if ((tcConfig == null) && (tcConfigUrl == null)) { + // + throw new SchedulerConfigException("Neither " + TC_CONFIG_PROP + " or " + TC_CONFIGURL_PROP + + " are set in your properties. Please define one of them"); + } + + final boolean isURLConfig = tcConfig == null; + TerracottaToolkitBuilder toolkitBuilder = new TerracottaToolkitBuilder(); + if (isURLConfig) { + toolkitBuilder.setTCConfigUrl(tcConfigUrl); + } else { + toolkitBuilder.setTCConfigSnippet(tcConfig); + } + if (rejoin != null) { + toolkitBuilder.setRejoin(rejoin); + } + toolkitBuilder.addTunnelledMBeanDomain("quartz"); + toolkit = (ToolkitInternal) toolkitBuilder.buildToolkit(); + + try { + realJobStore = getRealStore(toolkit); + } catch (Exception e) { + throw new SchedulerConfigException("Unable to create Terracotta client", e); + } + } + + abstract TerracottaJobStoreExtensions getRealStore(ToolkitInternal toolkitParam); + + public String getUUID() { + if (realJobStore == null) { + try { + init(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return realJobStore.getUUID(); + } + + public void setMisfireThreshold(long threshold) { + this.misFireThreshold = threshold; + } + + public void setTcRetryInterval(long tcRetryInterval) { + this.tcRetryInterval = tcRetryInterval; + } + + @Override + public List acquireNextTriggers(long noLaterThan, int maxCount, long timeWindow) + throws JobPersistenceException { + try { + return realJobStore.acquireNextTriggers(noLaterThan, maxCount, timeWindow); + } catch (RejoinException e) { + throw new JobPersistenceException("Trigger acquisition failed due to client rejoin", e); + } + } + + @Override + public List getCalendarNames() throws JobPersistenceException { + try { + return realJobStore.getCalendarNames(); + } catch (RejoinException e) { + throw new JobPersistenceException("Calendar name retrieval failed due to client rejoin", e); + } + } + + @Override + public List getJobGroupNames() throws JobPersistenceException { + try { + return realJobStore.getJobGroupNames(); + } catch (RejoinException e) { + throw new JobPersistenceException("Job name retrieval failed due to client rejoin", e); + } + } + + @Override + public Set getJobKeys(GroupMatcher matcher) throws JobPersistenceException { + try { + return realJobStore.getJobKeys(matcher); + } catch (RejoinException e) { + throw new JobPersistenceException("Job key retrieval failed due to client rejoin", e); + } + } + + @Override + public int getNumberOfCalendars() throws JobPersistenceException { + try { + return realJobStore.getNumberOfCalendars(); + } catch (RejoinException e) { + throw new JobPersistenceException("Calendar count retrieval failed due to client rejoin", e); + } + } + + @Override + public int getNumberOfJobs() throws JobPersistenceException { + try { + return realJobStore.getNumberOfJobs(); + } catch (RejoinException e) { + throw new JobPersistenceException("Job count retrieval failed due to client rejoin", e); + } + } + + @Override + public int getNumberOfTriggers() throws JobPersistenceException { + try { + return realJobStore.getNumberOfTriggers(); + } catch (RejoinException e) { + throw new JobPersistenceException("Trigger count retrieval failed due to client rejoin", e); + } + } + + @Override + public Set getPausedTriggerGroups() throws JobPersistenceException { + try { + return realJobStore.getPausedTriggerGroups(); + } catch (RejoinException e) { + throw new JobPersistenceException("Paused trigger group retrieval failed due to client rejoin", e); + } + } + + @Override + public List getTriggerGroupNames() throws JobPersistenceException { + try { + return realJobStore.getTriggerGroupNames(); + } catch (RejoinException e) { + throw new JobPersistenceException("Trigger group retrieval failed due to client rejoin", e); + } + } + + @Override + public Set getTriggerKeys(GroupMatcher matcher) throws JobPersistenceException { + try { + return realJobStore.getTriggerKeys(matcher); + } catch (RejoinException e) { + throw new JobPersistenceException("Trigger key retrieval failed due to client rejoin", e); + } + } + + @Override + public List getTriggersForJob(JobKey jobKey) throws JobPersistenceException { + try { + return realJobStore.getTriggersForJob(jobKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Trigger retrieval failed due to client rejoin", e); + } + } + + @Override + public Trigger.TriggerState getTriggerState(TriggerKey triggerKey) throws JobPersistenceException { + try { + return realJobStore.getTriggerState(triggerKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Trigger state retrieval failed due to client rejoin", e); + } + } + + @Override + public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler) throws SchedulerConfigException { + init(); + realJobStore.setInstanceId(schedInstId); + realJobStore.setInstanceName(schedName); + realJobStore.setTcRetryInterval(tcRetryInterval); + + if (misFireThreshold != null) { + realJobStore.setMisfireThreshold(misFireThreshold); + } + + if (synchWrite != null) { + realJobStore.setSynchronousWrite(synchWrite); + } + + if (estimatedTimeToReleaseAndAcquireTrigger != null) { + realJobStore.setEstimatedTimeToReleaseAndAcquireTrigger(estimatedTimeToReleaseAndAcquireTrigger); + } + + realJobStore.initialize(loadHelper, signaler); + } + + @Override + public void pauseAll() throws JobPersistenceException { + try { + realJobStore.pauseAll(); + } catch (RejoinException e) { + throw new JobPersistenceException("Pausing failed due to client rejoin", e); + } + } + + @Override + public void pauseJob(JobKey jobKey) throws JobPersistenceException { + try { + realJobStore.pauseJob(jobKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Pausing job failed due to client rejoin", e); + } + } + + @Override + public Collection pauseJobs(GroupMatcher matcher) throws JobPersistenceException { + try { + return realJobStore.pauseJobs(matcher); + } catch (RejoinException e) { + throw new JobPersistenceException("Pausing jobs failed due to client rejoin", e); + } + } + + @Override + public void pauseTrigger(TriggerKey triggerKey) throws JobPersistenceException { + try { + realJobStore.pauseTrigger(triggerKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Pausing trigger failed due to client rejoin", e); + } + } + + @Override + public Collection pauseTriggers(GroupMatcher matcher) throws JobPersistenceException { + try { + return realJobStore.pauseTriggers(matcher); + } catch (RejoinException e) { + throw new JobPersistenceException("Pausing triggers failed due to client rejoin", e); + } + } + + @Override + public void releaseAcquiredTrigger(OperableTrigger trigger) { + realJobStore.releaseAcquiredTrigger(trigger); + } + + @Override + public boolean removeCalendar(String calName) throws JobPersistenceException { + try { + return realJobStore.removeCalendar(calName); + } catch (RejoinException e) { + throw new JobPersistenceException("Removing calendar failed due to client rejoin", e); + } + } + + @Override + public boolean removeJob(JobKey jobKey) throws JobPersistenceException { + try { + return realJobStore.removeJob(jobKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Removing job failed due to client rejoin", e); + } + } + + @Override + public boolean removeTrigger(TriggerKey triggerKey) throws JobPersistenceException { + try { + return realJobStore.removeTrigger(triggerKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Removing trigger failed due to client rejoin", e); + } + } + + @Override + public boolean replaceTrigger(TriggerKey triggerKey, OperableTrigger newTrigger) throws JobPersistenceException { + try { + return realJobStore.replaceTrigger(triggerKey, newTrigger); + } catch (RejoinException e) { + throw new JobPersistenceException("Replacing trigger failed due to client rejoin", e); + } + } + + @Override + public void resumeAll() throws JobPersistenceException { + try { + realJobStore.resumeAll(); + } catch (RejoinException e) { + throw new JobPersistenceException("Resuming failed due to client rejoin", e); + } + } + + @Override + public void resumeJob(JobKey jobKey) throws JobPersistenceException { + try { + realJobStore.resumeJob(jobKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Reusming job failed due to client rejoin", e); + } + } + + @Override + public Collection resumeJobs(GroupMatcher matcher) throws JobPersistenceException { + try { + return realJobStore.resumeJobs(matcher); + } catch (RejoinException e) { + throw new JobPersistenceException("Resuming jobs failed due to client rejoin", e); + } + } + + @Override + public void resumeTrigger(TriggerKey triggerKey) throws JobPersistenceException { + try { + realJobStore.resumeTrigger(triggerKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Resuming trigger failed due to client rejoin", e); + } + } + + @Override + public Collection resumeTriggers(GroupMatcher matcher) throws JobPersistenceException { + try { + return realJobStore.resumeTriggers(matcher); + } catch (RejoinException e) { + throw new JobPersistenceException("Resuming triggers failed due to client rejoin", e); + } + } + + @Override + public Calendar retrieveCalendar(String calName) throws JobPersistenceException { + try { + return realJobStore.retrieveCalendar(calName); + } catch (RejoinException e) { + throw new JobPersistenceException("Calendar retrieval failed due to client rejoin", e); + } + } + + @Override + public JobDetail retrieveJob(JobKey jobKey) throws JobPersistenceException { + try { + return realJobStore.retrieveJob(jobKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Job retrieval failed due to client rejoin", e); + } + } + + @Override + public OperableTrigger retrieveTrigger(TriggerKey triggerKey) throws JobPersistenceException { + try { + return realJobStore.retrieveTrigger(triggerKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Trigger retrieval failed due to client rejoin", e); + } + } + + @Override + public void schedulerStarted() throws SchedulerException { + try { + realJobStore.schedulerStarted(); + } catch (RejoinException e) { + throw new JobPersistenceException("Scheduler start failed due to client rejoin", e); + } + } + + @Override + public void schedulerPaused() { + realJobStore.schedulerPaused(); + } + + @Override + public void schedulerResumed() { + realJobStore.schedulerResumed(); + } + + @Override + public void setInstanceId(String schedInstId) { + this.schedInstId = schedInstId; + } + + @Override + public void setInstanceName(String schedName) { + this.schedName = schedName; + } + + @Override + public void setThreadPoolSize(final int poolSize) { + realJobStore.setThreadPoolSize(poolSize); + } + + @Override + public void shutdown() { + if (realJobStore != null) { + realJobStore.shutdown(); + } + if (toolkit != null) { + toolkit.shutdown(); + } + } + + @Override + public void storeCalendar(String name, Calendar calendar, boolean replaceExisting, boolean updateTriggers) + throws ObjectAlreadyExistsException, JobPersistenceException { + try { + realJobStore.storeCalendar(name, calendar, replaceExisting, updateTriggers); + } catch (RejoinException e) { + throw new JobPersistenceException("Storing calendar failed due to client rejoin", e); + } + } + + @Override + public void storeJob(JobDetail newJob, boolean replaceExisting) throws ObjectAlreadyExistsException, + JobPersistenceException { + try { + realJobStore.storeJob(newJob, replaceExisting); + } catch (RejoinException e) { + throw new JobPersistenceException("Storing job failed due to client rejoin", e); + } + } + + @Override + public void storeJobAndTrigger(JobDetail newJob, OperableTrigger newTrigger) throws ObjectAlreadyExistsException, + JobPersistenceException { + try { + realJobStore.storeJobAndTrigger(newJob, newTrigger); + } catch (RejoinException e) { + throw new JobPersistenceException("Storing job and trigger failed due to client rejoin", e); + } + } + + @Override + public void storeTrigger(OperableTrigger newTrigger, boolean replaceExisting) throws ObjectAlreadyExistsException, + JobPersistenceException { + try { + realJobStore.storeTrigger(newTrigger, replaceExisting); + } catch (RejoinException e) { + throw new JobPersistenceException("Storing trigger failed due to client rejoin", e); + } + } + + @Override + public boolean supportsPersistence() { + return true; + } + + @Override + public void triggeredJobComplete(OperableTrigger trigger, JobDetail jobDetail, Trigger.CompletedExecutionInstruction instruction) { + realJobStore.triggeredJobComplete(trigger, jobDetail, instruction); + } + + @Override + public List triggersFired(List triggers) throws JobPersistenceException { + try { + return realJobStore.triggersFired(triggers); + } catch (RejoinException e) { + throw new JobPersistenceException("Trigger fire marking failed due to client rejoin", e); + } + } + + public void setTcConfig(String tcConfig) { + this.tcConfig = tcConfig.trim(); + } + + public void setTcConfigUrl(String tcConfigUrl) { + this.tcConfigUrl = tcConfigUrl.trim(); + } + + public void setSynchronousWrite(String synchWrite) { + this.synchWrite = synchWrite; + } + + public void setRejoin(String rejoin) { + this.rejoin = rejoin; + setSynchronousWrite(Boolean.TRUE.toString()); + } + + @Override + public long getEstimatedTimeToReleaseAndAcquireTrigger() { + return realJobStore.getEstimatedTimeToReleaseAndAcquireTrigger(); + } + + public void setEstimatedTimeToReleaseAndAcquireTrigger(long estimate) { + this.estimatedTimeToReleaseAndAcquireTrigger = estimate; + } + + @Override + public boolean isClustered() { + return true; + } + + @Override + public boolean checkExists(final JobKey jobKey) throws JobPersistenceException { + try { + return realJobStore.checkExists(jobKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Job existence check failed due to client rejoin", e); + } + } + + @Override + public boolean checkExists(final TriggerKey triggerKey) throws JobPersistenceException { + try { + return realJobStore.checkExists(triggerKey); + } catch (RejoinException e) { + throw new JobPersistenceException("Trigger existence check failed due to client rejoin", e); + } + } + + @Override + public void clearAllSchedulingData() throws JobPersistenceException { + try { + realJobStore.clearAllSchedulingData(); + } catch (RejoinException e) { + throw new JobPersistenceException("Scheduler data clear failed due to client rejoin", e); + } + } + + @Override + public boolean removeTriggers(List arg0) throws JobPersistenceException { + try { + return realJobStore.removeTriggers(arg0); + } catch (RejoinException e) { + throw new JobPersistenceException("Remvoing triggers failed due to client rejoin", e); + } + } + + @Override + public boolean removeJobs(List arg0) throws JobPersistenceException { + try { + return realJobStore.removeJobs(arg0); + } catch (RejoinException e) { + throw new JobPersistenceException("Removing jobs failed due to client rejoin", e); + } + } + + @Override + public void storeJobsAndTriggers(Map> arg0, boolean arg1) + throws ObjectAlreadyExistsException, JobPersistenceException { + try { + realJobStore.storeJobsAndTriggers(arg0, arg1); + } catch (RejoinException e) { + throw new JobPersistenceException("Store jobs and triggers failed due to client rejoin", e); + } + } + + protected TerracottaJobStoreExtensions getRealJobStore() { + return realJobStore; + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/ClusteredJobStore.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/ClusteredJobStore.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/ClusteredJobStore.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,31 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz; + +import org.quartz.spi.JobStore; +import org.terracotta.toolkit.cluster.ClusterListener; + +/** + * @author Alex Snaps + */ +public interface ClusteredJobStore extends JobStore, ClusterListener { + void setMisfireThreshold(long misfireThreshold); + + void setEstimatedTimeToReleaseAndAcquireTrigger(long estimate); + + void setTcRetryInterval(long retryInterval); +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/DefaultClusteredJobStore.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/DefaultClusteredJobStore.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/DefaultClusteredJobStore.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,2086 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. 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 org.terracotta.quartz; + +import org.quartz.Calendar; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.JobPersistenceException; +import org.quartz.ObjectAlreadyExistsException; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.TriggerKey; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.impl.matchers.StringMatcher; +import org.quartz.impl.matchers.StringMatcher.StringOperatorName; +import org.quartz.impl.triggers.SimpleTriggerImpl; +import org.quartz.spi.ClassLoadHelper; +import org.quartz.spi.OperableTrigger; +import org.quartz.spi.SchedulerSignaler; +import org.quartz.spi.TriggerFiredBundle; +import org.quartz.spi.TriggerFiredResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terracotta.quartz.collections.TimeTriggerSet; +import org.terracotta.quartz.collections.ToolkitDSHolder; +import org.terracotta.quartz.wrappers.DefaultWrapperFactory; +import org.terracotta.quartz.wrappers.FiredTrigger; +import org.terracotta.quartz.wrappers.JobFacade; +import org.terracotta.quartz.wrappers.JobWrapper; +import org.terracotta.quartz.wrappers.TriggerFacade; +import org.terracotta.quartz.wrappers.TriggerWrapper; +import org.terracotta.quartz.wrappers.TriggerWrapper.TriggerState; +import org.terracotta.quartz.wrappers.WrapperFactory; +import org.terracotta.toolkit.Toolkit; +import org.terracotta.toolkit.atomic.ToolkitTransactionType; +import org.terracotta.toolkit.store.ToolkitStore; +import org.terracotta.toolkit.cluster.ClusterEvent; +import org.terracotta.toolkit.cluster.ClusterInfo; +import org.terracotta.toolkit.cluster.ClusterNode; +import org.terracotta.toolkit.concurrent.locks.ToolkitLock; +import org.terracotta.toolkit.internal.ToolkitInternal; +import org.terracotta.toolkit.internal.concurrent.locks.ToolkitLockTypeInternal; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.terracotta.toolkit.rejoin.RejoinException; + +/** + *

+ * This class implements a {@link org.quartz.spi.JobStore} that utilizes Terracotta as its storage device. + *

+ *

+ * This code is largely a cut-n-paste of RamJobStore and the world would be a better place if the duplicate code could + * be refactored + *

+ */ + +class DefaultClusteredJobStore implements ClusteredJobStore { + private final ToolkitDSHolder toolkitDSHolder; + private final Toolkit toolkit; + + private final JobFacade jobFacade; + private final TriggerFacade triggerFacade; + private final TimeTriggerSet timeTriggers; + + private final ToolkitStore calendarsByName; + private long misfireThreshold = 60000L; + + private final ToolkitLockTypeInternal lockType; + private transient final ToolkitLock lock; + + private final ClusterInfo clusterInfo; + private final WrapperFactory wrapperFactory; + + private long ftrCtr; + private volatile SchedulerSignaler signaler; + private final Logger logger; + private volatile String terracottaClientId; + private long estimatedTimeToReleaseAndAcquireTrigger = 15L; + private volatile LocalLockState localStateLock; + private volatile TriggerRemovedFromCandidateFiringListHandler triggerRemovedFromCandidateFiringListHandler; + private volatile boolean toolkitShutdown; + private long retryInterval; + + // This is a hack to prevent certain objects from ever being flushed. "this" should never be flushed (at least not + // until the scheduler is shutdown) since it is referenced from the scheduler (which is not a shared object) + // private transient Set hardRefs = new HashSet(); + + public DefaultClusteredJobStore(boolean synchWrite, Toolkit toolkit, String jobStoreName) { + this(synchWrite, toolkit, jobStoreName, new ToolkitDSHolder(jobStoreName, toolkit), new DefaultWrapperFactory()); + } + + public DefaultClusteredJobStore(boolean synchWrite, Toolkit toolkit, String jobStoreName, + ToolkitDSHolder toolkitDSHolder, WrapperFactory wrapperFactory) { + this.toolkit = toolkit; + this.wrapperFactory = wrapperFactory; + this.clusterInfo = toolkit.getClusterInfo(); + + this.toolkitDSHolder = toolkitDSHolder; + + this.jobFacade = new JobFacade(toolkitDSHolder); + this.triggerFacade = new TriggerFacade(toolkitDSHolder); + + this.timeTriggers = toolkitDSHolder.getOrCreateTimeTriggerSet(); + this.calendarsByName = toolkitDSHolder.getOrCreateCalendarWrapperMap(); + + this.lockType = synchWrite ? ToolkitLockTypeInternal.SYNCHRONOUS_WRITE : ToolkitLockTypeInternal.WRITE; + ToolkitTransactionType txnType = synchWrite ? ToolkitTransactionType.SYNC : ToolkitTransactionType.NORMAL; + this.lock = new TransactionControllingLock((ToolkitInternal) toolkit, toolkitDSHolder.getLock(lockType), txnType); + + this.logger = LoggerFactory.getLogger(getClass()); + + getLog().info("Synchronous write locking is [" + synchWrite + "]"); + } + + // private synchronized boolean hardRef(Object obj) { + // if (hardRefs == null) { + // hardRefs = new HashSet(); + // } + // + // return hardRefs.add(obj); + // } + + private Logger getLog() { + return logger; + } + + private void disable() { + toolkitShutdown = true; + try { + getLocalLockState().disableLocking(); + } catch (InterruptedException e) { + getLog().error("failed to disable the job store", e); + } + } + + private LocalLockState getLocalLockState() { + LocalLockState rv = localStateLock; + if (rv != null) return rv; + + synchronized (DefaultClusteredJobStore.class) { + if (localStateLock == null) { + localStateLock = new LocalLockState(); + } + return localStateLock; + } + } + + void lock() throws JobPersistenceException { + getLocalLockState().attemptAcquireBegin(); + try { + lock.lock(); + } catch (RejoinException e) { + getLocalLockState().release(); + throw e; + } + } + + void unlock() { + try { + lock.unlock(); + } finally { + getLocalLockState().release(); + } + } + + /** + *

+ * Called by the QuartzScheduler before the JobStore is used, in order to give the it a chance to + * initialize. + *

+ */ + + @Override + // XXX: remove this suppression + @SuppressWarnings("unchecked") + public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler schedulerSignaler) { + this.terracottaClientId = clusterInfo.getCurrentNode().getId(); + this.ftrCtr = System.currentTimeMillis(); + + // this MUST happen before initializing the trigger set (otherwise we might receive an update which get an NPE) + // this.serializer.setClassLoadHelper(loadHelper); + + this.signaler = schedulerSignaler; + + getLog().info(getClass().getSimpleName() + " initialized."); + + ((ToolkitInternal) toolkit).registerBeforeShutdownHook(new ShutdownHook(this)); + } + + @Override + public void schedulerStarted() throws SchedulerException { + clusterInfo.addClusterListener(this); + + Collection nodes = clusterInfo.getNodes(); + + Set activeClientIDs = new HashSet(); + for (ClusterNode node : nodes) { + boolean added = activeClientIDs.add(node.getId()); + if (!added) { + getLog().error("DUPLICATE node ID detected: " + node); + } + } + + lock(); + try { + List toEval = new ArrayList(); + + // scan for orphaned triggers + for (TriggerKey triggerKey : triggerFacade.allTriggerKeys()) { + TriggerWrapper tw = triggerFacade.get(triggerKey); + String lastTerracotaClientId = tw.getLastTerracotaClientId(); + if (lastTerracotaClientId == null) { + continue; + } + + if (!activeClientIDs.contains(lastTerracotaClientId) || tw.getState() == TriggerState.ERROR) { + toEval.add(tw); + } + } + + for (TriggerWrapper tw : toEval) { + evalOrphanedTrigger(tw, true); + } + + // scan firedTriggers + for (Iterator iter = triggerFacade.allFiredTriggers().iterator(); iter.hasNext();) { + FiredTrigger ft = iter.next(); + if (!activeClientIDs.contains(ft.getClientId())) { + getLog().info("Found non-complete fired trigger: " + ft); + iter.remove(); + + TriggerWrapper tw = triggerFacade.get(ft.getTriggerKey()); + if (tw == null) { + getLog().error("no trigger found for executing trigger: " + ft.getTriggerKey()); + continue; + } + + scheduleRecoveryIfNeeded(tw, ft); + } + } + } finally { + unlock(); + } + } + + @Override + public void schedulerPaused() { + // do nothing + } + + @Override + public void schedulerResumed() { + // do nothing + } + + private void evalOrphanedTrigger(TriggerWrapper tw, boolean newNode) { + getLog().info("Evaluating orphaned trigger " + tw); + + JobWrapper jobWrapper = jobFacade.get(tw.getJobKey()); + + if (jobWrapper == null) { + getLog().error("No job found for orphaned trigger: " + tw); + // even if it was deleted, there may be cleanup to do + jobFacade.removeBlockedJob(tw.getJobKey()); + return; + } + + if (newNode && tw.getState() == TriggerState.ERROR) { + tw.setState(TriggerState.WAITING, terracottaClientId, triggerFacade); + timeTriggers.add(tw); + } + + if (tw.getState() == TriggerState.BLOCKED) { + tw.setState(TriggerState.WAITING, terracottaClientId, triggerFacade); + timeTriggers.add(tw); + } else if (tw.getState() == TriggerState.PAUSED_BLOCKED) { + tw.setState(TriggerState.PAUSED, terracottaClientId, triggerFacade); + } + + if (tw.getState() == TriggerState.ACQUIRED) { + tw.setState(TriggerState.WAITING, terracottaClientId, triggerFacade); + timeTriggers.add(tw); + } + + if (!tw.mayFireAgain() && !jobWrapper.requestsRecovery()) { + try { + removeTrigger(tw.getKey()); + } catch (JobPersistenceException e) { + getLog().error("Can't remove completed trigger (and related job) " + tw, e); + } + } + + if (jobWrapper.isConcurrentExectionDisallowed()) { + jobFacade.removeBlockedJob(jobWrapper.getKey()); + List triggersForJob = triggerFacade.getTriggerWrappersForJob(jobWrapper.getKey()); + + for (TriggerWrapper trigger : triggersForJob) { + if (trigger.getState() == TriggerState.BLOCKED) { + trigger.setState(TriggerState.WAITING, terracottaClientId, triggerFacade); + timeTriggers.add(trigger); + } else if (trigger.getState() == TriggerState.PAUSED_BLOCKED) { + trigger.setState(TriggerState.PAUSED, terracottaClientId, triggerFacade); + } + } + } + } + + private void scheduleRecoveryIfNeeded(TriggerWrapper tw, FiredTrigger recovering) { + JobWrapper jobWrapper = jobFacade.get(tw.getJobKey()); + + if (jobWrapper == null) { + getLog().error("No job found for orphaned trigger: " + tw); + return; + } + + if (jobWrapper.requestsRecovery()) { + OperableTrigger recoveryTrigger = createRecoveryTrigger(tw, jobWrapper, "recover_" + terracottaClientId + "_" + + ftrCtr++, recovering); + + JobDataMap jd = tw.getTriggerClone().getJobDataMap(); + jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_NAME, tw.getKey().getName()); + jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_GROUP, tw.getKey().getGroup()); + jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_FIRETIME_IN_MILLISECONDS, String.valueOf(recovering.getFireTime())); + jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_SCHEDULED_FIRETIME_IN_MILLISECONDS, String.valueOf(recovering.getScheduledFireTime())); + + recoveryTrigger.setJobDataMap(jd); + recoveryTrigger.computeFirstFireTime(null); + + try { + storeTrigger(recoveryTrigger, false); + if (!tw.mayFireAgain()) { + removeTrigger(tw.getKey()); + } + getLog().info("Recovered job " + jobWrapper + " for trigger " + tw); + } catch (JobPersistenceException e) { + getLog().error("Can't recover job " + jobWrapper + " for trigger " + tw, e); + } + } + } + + protected OperableTrigger createRecoveryTrigger(TriggerWrapper tw, JobWrapper jw, + String name, FiredTrigger recovering) { + final SimpleTriggerImpl recoveryTrigger = new SimpleTriggerImpl(name, Scheduler.DEFAULT_RECOVERY_GROUP, new Date(recovering.getScheduledFireTime())); + recoveryTrigger.setJobName(jw.getKey().getName()); + recoveryTrigger.setJobGroup(jw.getKey().getGroup()); + recoveryTrigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY); + recoveryTrigger.setPriority(tw.getPriority()); + return recoveryTrigger; + } + + private long getMisfireThreshold() { + return misfireThreshold; + } + + /** + * The number of milliseconds by which a trigger must have missed its next-fire-time, in order for it to be considered + * "misfired" and thus have its misfire instruction applied. + * + * @param misfireThreshold + */ + @Override + public void setMisfireThreshold(long misfireThreshold) { + if (misfireThreshold < 1) { throw new IllegalArgumentException("Misfirethreashold must be larger than 0"); } + this.misfireThreshold = misfireThreshold; + } + + /** + *

+ * Called by the QuartzScheduler to inform the JobStore that it should free up all of it's resources + * because the scheduler is shutting down. + *

+ */ + @Override + public void shutdown() { + // nothing to do + } + + @Override + public boolean supportsPersistence() { + // We throw an assertion here since this method should never be called directly on this instance. + throw new AssertionError(); + } + + /** + *

+ * Store the given {@link org.quartz.JobDetail} and {@link org.quartz.Trigger}. + *

+ * + * @param newJob The JobDetail to be stored. + * @param newTrigger The Trigger to be stored. + * @throws ObjectAlreadyExistsException if a Job with the same name/group already exists. + */ + @Override + public void storeJobAndTrigger(JobDetail newJob, OperableTrigger newTrigger) throws JobPersistenceException { + lock(); + try { + storeJob(newJob, false); + storeTrigger(newTrigger, false); + } finally { + unlock(); + } + } + + /** + *

+ * Store the given {@link org.quartz.Job}. + *

+ * + * @param newJob The 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. + */ + @Override + public void storeJob(JobDetail newJob, boolean replaceExisting) throws ObjectAlreadyExistsException, + JobPersistenceException { + JobDetail clone = (JobDetail) newJob.clone(); + + lock(); + try { + // wrapper construction must be done in lock since serializer is unlocked + JobWrapper jw = wrapperFactory.createJobWrapper(clone); + + if (jobFacade.containsKey(jw.getKey())) { + if (!replaceExisting) { throw new ObjectAlreadyExistsException(newJob); } + } else { + // get job group + Set grpSet = toolkitDSHolder.getOrCreateJobsGroupMap(newJob.getKey().getGroup()); + // add to jobs by group + grpSet.add(jw.getKey().getName()); + + if (!jobFacade.hasGroup(jw.getKey().getGroup())) { + jobFacade.addGroup(jw.getKey().getGroup()); + } + } + + // add/update jobs FQN map + jobFacade.put(jw.getKey(), jw); + } finally { + unlock(); + } + } + + /** + *

+ * Remove (delete) the {@link org.quartz.Job} with the given name, and any + * {@link org.quartz.Trigger} s that reference it. + *

+ * + * @param jobKey The key of the Job to be removed. + * @return true if a Job with the given name & group was found and removed from the store. + */ + @Override + public boolean removeJob(JobKey jobKey) throws JobPersistenceException { + boolean found = false; + lock(); + try { + List trigger = getTriggersForJob(jobKey); + for (OperableTrigger trig : trigger) { + this.removeTrigger(trig.getKey()); + found = true; + } + + found = (jobFacade.remove(jobKey) != null) | found; + if (found) { + Set grpSet = toolkitDSHolder.getOrCreateJobsGroupMap(jobKey.getGroup()); + grpSet.remove(jobKey.getName()); + if (grpSet.isEmpty()) { + toolkitDSHolder.removeJobsGroupMap(jobKey.getGroup()); + jobFacade.removeGroup(jobKey.getGroup()); + } + } + } finally { + unlock(); + } + + return found; + } + + @Override + public boolean removeJobs(List jobKeys) throws JobPersistenceException { + boolean allFound = true; + + lock(); + try { + for (JobKey key : jobKeys) + allFound = removeJob(key) && allFound; + } finally { + unlock(); + } + + return allFound; + } + + @Override + public boolean removeTriggers(List triggerKeys) throws JobPersistenceException { + boolean allFound = true; + + lock(); + try { + for (TriggerKey key : triggerKeys) + allFound = removeTrigger(key) && allFound; + } finally { + unlock(); + } + + return allFound; + } + + @Override + public void storeJobsAndTriggers(Map> triggersAndJobs, boolean replace) + throws ObjectAlreadyExistsException, JobPersistenceException { + + lock(); + try { + // make sure there are no collisions... + if (!replace) { + for (JobDetail job : triggersAndJobs.keySet()) { + if (checkExists(job.getKey())) throw new ObjectAlreadyExistsException(job); + for (Trigger trigger : triggersAndJobs.get(job)) { + if (checkExists(trigger.getKey())) throw new ObjectAlreadyExistsException(trigger); + } + } + } + // do bulk add... + for (JobDetail job : triggersAndJobs.keySet()) { + storeJob(job, true); + for (Trigger trigger : triggersAndJobs.get(job)) { + storeTrigger((OperableTrigger) trigger, true); + } + } + } finally { + unlock(); + } + } + + /** + *

+ * Store the given {@link org.quartz.Trigger}. + *

+ * + * @param newTrigger The 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 #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) + */ + @Override + public void storeTrigger(OperableTrigger newTrigger, boolean replaceExisting) throws JobPersistenceException { + OperableTrigger clone = (OperableTrigger) newTrigger.clone(); + + lock(); + try { + JobDetail job = retrieveJob(newTrigger.getJobKey()); + if (job == null) { + // + throw new JobPersistenceException("The job (" + newTrigger.getJobKey() + + ") referenced by the trigger does not exist."); + } + + // wrapper construction must be done in lock since serializer is unlocked + TriggerWrapper tw = wrapperFactory.createTriggerWrapper(clone, job.isConcurrentExectionDisallowed()); + + if (triggerFacade.containsKey(tw.getKey())) { + if (!replaceExisting) { throw new ObjectAlreadyExistsException(newTrigger); } + + removeTrigger(newTrigger.getKey(), false); + } + + // add to triggers by group + Set grpSet = toolkitDSHolder.getOrCreateTriggersGroupMap(newTrigger.getKey().getGroup()); + grpSet.add(newTrigger.getKey().getName()); + if (!triggerFacade.hasGroup(newTrigger.getKey().getGroup())) { + triggerFacade.addGroup(newTrigger.getKey().getGroup()); + } + + if (triggerFacade.pausedGroupsContain(newTrigger.getKey().getGroup()) + || jobFacade.pausedGroupsContain(newTrigger.getJobKey().getGroup())) { + tw.setState(TriggerState.PAUSED, terracottaClientId, triggerFacade); + if (jobFacade.blockedJobsContain(tw.getJobKey())) { + tw.setState(TriggerState.PAUSED_BLOCKED, terracottaClientId, triggerFacade); + } + } else if (jobFacade.blockedJobsContain(tw.getJobKey())) { + tw.setState(TriggerState.BLOCKED, terracottaClientId, triggerFacade); + } else { + timeTriggers.add(tw); + } + + // add to triggers by FQN map + triggerFacade.put(tw.getKey(), tw); + } finally { + unlock(); + } + } + + /** + *

+ * Remove (delete) the {@link org.quartz.Trigger} with the given name. + *

+ * + * @param triggerKey The key of the Trigger to be removed. + * @return true if a Trigger with the given name & group was found and removed from the + * store. + */ + @Override + public boolean removeTrigger(TriggerKey triggerKey) throws JobPersistenceException { + return removeTrigger(triggerKey, true); + } + + private boolean removeTrigger(TriggerKey triggerKey, boolean removeOrphanedJob) throws JobPersistenceException { + + lock(); + TriggerWrapper tw = null; + try { + // remove from triggers by FQN map + tw = triggerFacade.remove(triggerKey); + + if (tw != null) { + // remove from triggers by group + Set grpSet = toolkitDSHolder.getOrCreateTriggersGroupMap(triggerKey.getGroup()); + grpSet.remove(triggerKey.getName()); + if (grpSet.size() == 0) { + toolkitDSHolder.removeTriggersGroupMap(triggerKey.getGroup()); + triggerFacade.removeGroup(triggerKey.getGroup()); + } + // remove from triggers array + timeTriggers.remove(tw); + + if (removeOrphanedJob) { + JobWrapper jw = jobFacade.get(tw.getJobKey()); + List trigs = getTriggersForJob(tw.getJobKey()); + if ((trigs == null || trigs.size() == 0) && !jw.isDurable()) { + JobKey jobKey = tw.getJobKey(); + if (removeJob(jobKey)) { + signaler.notifySchedulerListenersJobDeleted(jobKey); + } + } + } + } + } finally { + unlock(); + } + + return tw != null; + } + + /** + * @see org.quartz.spi.JobStore#replaceTrigger + */ + @Override + public boolean replaceTrigger(TriggerKey triggerKey, OperableTrigger newTrigger) throws JobPersistenceException { + boolean found = false; + + lock(); + try { + // remove from triggers by FQN map + TriggerWrapper tw = triggerFacade.remove(triggerKey); + found = tw != null; + + if (tw != null) { + if (!tw.getJobKey().equals(newTrigger.getJobKey())) { throw new JobPersistenceException( + "New trigger is not related to the same job as the old trigger."); } + // remove from triggers by group + Set grpSet = toolkitDSHolder.getOrCreateTriggersGroupMap(triggerKey.getGroup()); + grpSet.remove(triggerKey.getName()); + if (grpSet.size() == 0) { + toolkitDSHolder.removeTriggersGroupMap(triggerKey.getGroup()); + triggerFacade.removeGroup(triggerKey.getGroup()); + } + timeTriggers.remove(tw); + + try { + storeTrigger(newTrigger, false); + } catch (JobPersistenceException jpe) { + storeTrigger(tw.getTriggerClone(), false); // put previous trigger back... + throw jpe; + } + } + } finally { + unlock(); + } + + return found; + } + + /** + *

+ * Retrieve the {@link org.quartz.JobDetail} for the given {@link org.quartz.Job}. + *

+ * + * @param jobKey The key of the Job to be retrieved. + * @return The desired Job, or null if there is no match. + */ + @Override + public JobDetail retrieveJob(JobKey jobKey) throws JobPersistenceException { + JobWrapper jobWrapper = getJob(jobKey); + return jobWrapper == null ? null : (JobDetail) jobWrapper.getJobDetailClone(); + } + + JobWrapper getJob(final JobKey key) throws JobPersistenceException { + lock(); + try { + return jobFacade.get(key); + } finally { + unlock(); + } + } + + /** + *

+ * Retrieve the given {@link org.quartz.Trigger}. + *

+ * + * @param triggerKey The key of the Trigger to be retrieved. + * @return The desired Trigger, or null if there is no match. + */ + @Override + public OperableTrigger retrieveTrigger(TriggerKey triggerKey) throws JobPersistenceException { + lock(); + try { + TriggerWrapper tw = triggerFacade.get(triggerKey); + return (tw != null) ? (OperableTrigger) tw.getTriggerClone() : null; + } finally { + unlock(); + } + } + + @Override + public boolean checkExists(final JobKey jobKey) { + return jobFacade.containsKey(jobKey); + } + + /** + * {@inheritDoc} + * + * @throws JobPersistenceException + */ + @Override + public boolean checkExists(final TriggerKey triggerKey) throws JobPersistenceException { + return triggerFacade.containsKey(triggerKey); + } + + @Override + public void clearAllSchedulingData() throws JobPersistenceException { + lock(); + try { + // unschedule jobs (delete triggers) + List lst = getTriggerGroupNames(); + for (String group : lst) { + Set keys = getTriggerKeys(GroupMatcher.triggerGroupEquals(group)); + for (TriggerKey key : keys) { + removeTrigger(key); + } + } + // delete jobs + lst = getJobGroupNames(); + for (String group : lst) { + Set keys = getJobKeys(GroupMatcher.jobGroupEquals(group)); + for (JobKey key : keys) { + removeJob(key); + } + } + // delete calendars + lst = getCalendarNames(); + for (String name : lst) { + removeCalendar(name); + } + } finally { + unlock(); + } + } + + /** + *

+ * Get the current state of the identified {@link Trigger}. + *

+ * + * @see Trigger.TriggerState + */ + @Override + public Trigger.TriggerState getTriggerState(org.quartz.TriggerKey key) throws JobPersistenceException { + + TriggerWrapper tw; + lock(); + try { + tw = triggerFacade.get(key); + } finally { + unlock(); + } + + if (tw == null) { return Trigger.TriggerState.NONE; } + + if (tw.getState() == TriggerState.COMPLETE) { return Trigger.TriggerState.COMPLETE; } + + if (tw.getState() == TriggerState.PAUSED) { return Trigger.TriggerState.PAUSED; } + + if (tw.getState() == TriggerState.PAUSED_BLOCKED) { return Trigger.TriggerState.PAUSED; } + + if (tw.getState() == TriggerState.BLOCKED) { return Trigger.TriggerState.BLOCKED; } + + if (tw.getState() == TriggerState.ERROR) { return Trigger.TriggerState.ERROR; } + + return Trigger.TriggerState.NORMAL; + } + + /** + *

+ * Store the given {@link org.quartz.Calendar}. + *

+ * + * @param calendar The 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 Triggers 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. + */ + @Override + public void storeCalendar(String name, Calendar calendar, boolean replaceExisting, boolean updateTriggers) + throws ObjectAlreadyExistsException, JobPersistenceException { + + Calendar clone = (Calendar) calendar.clone(); + + lock(); + try { + Calendar cal = calendarsByName.get(name); + + if (cal != null && replaceExisting == false) { + throw new ObjectAlreadyExistsException("Calendar with name '" + name + "' already exists."); + } else if (cal != null) { + calendarsByName.remove(name); + } + + Calendar cw = clone; + calendarsByName.putNoReturn(name, cw); + + if (cal != null && updateTriggers) { + for (TriggerWrapper tw : triggerFacade.getTriggerWrappersForCalendar(name)) { + boolean removed = timeTriggers.remove(tw); + + tw.updateWithNewCalendar(clone, getMisfireThreshold(), triggerFacade); + + if (removed) { + timeTriggers.add(tw); + } + } + } + } finally { + unlock(); + } + } + + /** + *

+ * Remove (delete) the {@link org.quartz.Calendar} with the given name. + *

+ *

+ * If removal of the Calendar would result in s pointing to non-existent calendars, + * then a JobPersistenceException will be thrown. + *

+ * * + * + * @param calName The name of the Calendar to be removed. + * @return true if a Calendar with the given name was found and removed from the store. + */ + @Override + public boolean removeCalendar(String calName) throws JobPersistenceException { + int numRefs = 0; + + lock(); + try { + for (TriggerKey triggerKey : triggerFacade.allTriggerKeys()) { + TriggerWrapper tw = triggerFacade.get(triggerKey); + if (tw.getCalendarName() != null && tw.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); + } finally { + unlock(); + } + } + + /** + *

+ * Retrieve the given {@link org.quartz.Trigger}. + *

+ * + * @param calName The name of the Calendar to be retrieved. + * @return The desired Calendar, or null if there is no match. + */ + @Override + public Calendar retrieveCalendar(String calName) throws JobPersistenceException { + lock(); + try { + Calendar cw = calendarsByName.get(calName); + return (Calendar) (cw == null ? null : cw.clone()); + } finally { + unlock(); + } + } + + /** + *

+ * Get the number of {@link org.quartz.JobDetail} s that are stored in the JobsStore. + *

+ */ + @Override + public int getNumberOfJobs() throws JobPersistenceException { + lock(); + try { + return jobFacade.numberOfJobs(); + } finally { + unlock(); + } + } + + /** + *

+ * Get the number of {@link org.quartz.Trigger} s that are stored in the JobsStore. + *

+ */ + @Override + public int getNumberOfTriggers() throws JobPersistenceException { + lock(); + try { + return triggerFacade.numberOfTriggers(); + } finally { + unlock(); + } + } + + /** + *

+ * Get the number of {@link org.quartz.Calendar} s that are stored in the JobsStore. + *

+ */ + @Override + public int getNumberOfCalendars() throws JobPersistenceException { + lock(); + try { + return calendarsByName.size(); + } finally { + unlock(); + } + } + + /** + *

+ * Get the names of all of the {@link org.quartz.Job} s that have the given group name. + *

+ */ + @Override + public Set getJobKeys(GroupMatcher matcher) throws JobPersistenceException { + lock(); + try { + Set matchingGroups = new HashSet(); + switch (matcher.getCompareWithOperator()) { + case EQUALS: + matchingGroups.add(matcher.getCompareToValue()); + break; + default: + for (String group : jobFacade.getAllGroupNames()) { + if (matcher.getCompareWithOperator().evaluate(group, matcher.getCompareToValue())) { + matchingGroups.add(group); + } + } + } + + Set out = new HashSet(); + for (String matchingGroup : matchingGroups) { + + Set grpJobNames = toolkitDSHolder.getOrCreateJobsGroupMap(matchingGroup); + for (String jobName : grpJobNames) { + JobKey jobKey = new JobKey(jobName, matchingGroup); + if (jobFacade.containsKey(jobKey)) { + out.add(jobKey); + } + } + } + + return out; + } finally { + unlock(); + } + } + + /** + *

+ * Get the names of all of the {@link 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 + * ). + *

+ */ + @Override + public List getCalendarNames() throws JobPersistenceException { + lock(); + try { + Set names = calendarsByName.keySet(); + return new ArrayList(names); + } finally { + unlock(); + } + } + + /** + *

+ * Get the names of all of the {@link org.quartz.Trigger} s that have the given group name. + *

+ */ + @Override + public Set getTriggerKeys(GroupMatcher matcher) throws JobPersistenceException { + lock(); + try { + Set groupNames = new HashSet(); + switch (matcher.getCompareWithOperator()) { + case EQUALS: + groupNames.add(matcher.getCompareToValue()); + break; + default: + for (String group : triggerFacade.allTriggersGroupNames()) { + if (matcher.getCompareWithOperator().evaluate(group, matcher.getCompareToValue())) { + groupNames.add(group); + } + } + } + + Set out = new HashSet(); + + for (String groupName : groupNames) { + Set grpSet = toolkitDSHolder.getOrCreateTriggersGroupMap(groupName); + + for (String key : grpSet) { + TriggerKey triggerKey = new TriggerKey(key, groupName); + TriggerWrapper tw = triggerFacade.get(triggerKey); + if (tw != null) { + out.add(triggerKey); + } + } + } + + return out; + } finally { + unlock(); + } + } + + /** + *

+ * Get the names of all of the {@link org.quartz.Job} groups. + *

+ */ + @Override + public List getJobGroupNames() throws JobPersistenceException { + lock(); + try { + return new ArrayList(jobFacade.getAllGroupNames()); + } finally { + unlock(); + } + } + + /** + *

+ * Get the names of all of the {@link org.quartz.Trigger} groups. + *

+ */ + @Override + public List getTriggerGroupNames() throws JobPersistenceException { + lock(); + try { + return new ArrayList(triggerFacade.allTriggersGroupNames()); + } finally { + unlock(); + } + } + + /** + *

+ * Get all of the Triggers that are associated to the given Job. + *

+ *

+ * If there are no matches, a zero-length array should be returned. + *

+ */ + @Override + public List getTriggersForJob(final JobKey jobKey) throws JobPersistenceException { + List trigList = new ArrayList(); + + lock(); + try { + for (TriggerKey triggerKey : triggerFacade.allTriggerKeys()) { + TriggerWrapper tw = triggerFacade.get(triggerKey); + if (tw.getJobKey().equals(jobKey)) { + trigList.add(tw.getTriggerClone()); + } + } + } finally { + unlock(); + } + + return trigList; + } + + /** + *

+ * Pause the {@link Trigger} with the given name. + *

+ */ + @Override + public void pauseTrigger(TriggerKey triggerKey) throws JobPersistenceException { + lock(); + try { + TriggerWrapper tw = triggerFacade.get(triggerKey); + + // does the trigger exist? + if (tw == null) { return; } + + // if the trigger is "complete" pausing it does not make sense... + if (tw.getState() == TriggerState.COMPLETE) { return; } + + if (tw.getState() == TriggerState.BLOCKED) { + tw.setState(TriggerState.PAUSED_BLOCKED, terracottaClientId, triggerFacade); + } else { + tw.setState(TriggerState.PAUSED, terracottaClientId, triggerFacade); + } + + timeTriggers.remove(tw); + if (triggerRemovedFromCandidateFiringListHandler != null) { + triggerRemovedFromCandidateFiringListHandler.removeCandidateTrigger(tw); + } + } finally { + unlock(); + } + } + + /** + *

+ * 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. + *

+ */ + @Override + public Collection pauseTriggers(GroupMatcher matcher) throws JobPersistenceException { + HashSet pausedGroups = new HashSet(); + lock(); + try { + Set triggerKeys = getTriggerKeys(matcher); + for (TriggerKey key : triggerKeys) { + triggerFacade.addPausedGroup(key.getGroup()); + pausedGroups.add(key.getGroup()); + pauseTrigger(key); + } + // make sure to account for an exact group match for a group that doesn't yet exist + StringMatcher.StringOperatorName operator = matcher.getCompareWithOperator(); + if (operator.equals(StringOperatorName.EQUALS)) { + triggerFacade.addPausedGroup(matcher.getCompareToValue()); + pausedGroups.add(matcher.getCompareToValue()); + } + } finally { + unlock(); + } + return pausedGroups; + } + + /** + *

+ * Pause the {@link org.quartz.JobDetail} with the given name - by pausing all of its current + * Triggers. + *

+ */ + @Override + public void pauseJob(JobKey jobKey) throws JobPersistenceException { + lock(); + try { + for (OperableTrigger trigger : getTriggersForJob(jobKey)) { + pauseTrigger(trigger.getKey()); + } + } finally { + unlock(); + } + } + + /** + *

+ * Pause all of the {@link org.quartz.JobDetail}s in the given group - by pausing all of their + * Triggers. + *

+ *

+ * 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. + *

+ */ + @Override + public Collection pauseJobs(GroupMatcher matcher) throws JobPersistenceException { + Collection pausedGroups = new HashSet(); + lock(); + try { + + Set jobKeys = getJobKeys(matcher); + + for (JobKey jobKey : jobKeys) { + for (OperableTrigger trigger : getTriggersForJob(jobKey)) { + pauseTrigger(trigger.getKey()); + } + pausedGroups.add(jobKey.getGroup()); + } + // make sure to account for an exact group match for a group that doesn't yet exist + StringMatcher.StringOperatorName operator = matcher.getCompareWithOperator(); + if (operator.equals(StringOperatorName.EQUALS)) { + jobFacade.addPausedGroup(matcher.getCompareToValue()); + pausedGroups.add(matcher.getCompareToValue()); + } + } finally { + unlock(); + } + return pausedGroups; + } + + /** + *

+ * 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. + *

+ */ + @Override + public void resumeTrigger(TriggerKey triggerKey) throws JobPersistenceException { + lock(); + try { + TriggerWrapper tw = triggerFacade.get(triggerKey); + + // does the trigger exist? + if (tw == null) { return; } + + // if the trigger is not paused resuming it does not make sense... + if (tw.getState() != TriggerState.PAUSED && tw.getState() != TriggerState.PAUSED_BLOCKED) { return; } + + if (jobFacade.blockedJobsContain(tw.getJobKey())) { + tw.setState(TriggerState.BLOCKED, terracottaClientId, triggerFacade); + } else { + tw.setState(TriggerState.WAITING, terracottaClientId, triggerFacade); + } + + applyMisfire(tw); + + if (tw.getState() == TriggerState.WAITING) { + timeTriggers.add(tw); + } + } finally { + unlock(); + } + } + + /** + *

+ * 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. + *

+ */ + @Override + public Collection resumeTriggers(GroupMatcher matcher) throws JobPersistenceException { + Collection groups = new HashSet(); + lock(); + try { + Set triggerKeys = getTriggerKeys(matcher); + + for (TriggerKey triggerKey : triggerKeys) { + TriggerWrapper tw = triggerFacade.get(triggerKey); + if (tw != null) { + String jobGroup = tw.getJobKey().getGroup(); + + if (jobFacade.pausedGroupsContain(jobGroup)) { + continue; + } + groups.add(triggerKey.getGroup()); + } + resumeTrigger(triggerKey); + } + triggerFacade.removeAllPausedGroups(groups); + } finally { + unlock(); + } + return groups; + } + + /** + *

+ * Resume (un-pause) the {@link 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. + *

+ */ + @Override + public void resumeJob(JobKey jobKey) throws JobPersistenceException { + + lock(); + try { + for (OperableTrigger trigger : getTriggersForJob(jobKey)) { + resumeTrigger(trigger.getKey()); + } + } finally { + unlock(); + } + } + + /** + *

+ * Resume (un-pause) all of the {@link 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. + *

+ */ + @Override + public Collection resumeJobs(GroupMatcher matcher) throws JobPersistenceException { + Collection groups = new HashSet(); + lock(); + try { + Set jobKeys = getJobKeys(matcher); + + for (JobKey jobKey : jobKeys) { + if (groups.add(jobKey.getGroup())) { + jobFacade.removePausedJobGroup(jobKey.getGroup()); + } + for (OperableTrigger trigger : getTriggersForJob(jobKey)) { + resumeTrigger(trigger.getKey()); + } + } + } finally { + unlock(); + } + return groups; + } + + /** + *

+ * Pause all triggers - equivalent of calling pauseTriggerGroup(group) on every group. + *

+ *

+ * When resumeAll() is called (to un-pause), trigger misfire instructions WILL be applied. + *

+ * + * @see #resumeAll() + * @see #pauseTriggers(org.quartz.impl.matchers.GroupMatcher) + */ + @Override + public void pauseAll() throws JobPersistenceException { + + lock(); + try { + List names = getTriggerGroupNames(); + + for (String name : names) { + pauseTriggers(GroupMatcher.triggerGroupEquals(name)); + } + } finally { + unlock(); + } + } + + /** + *

+ * 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. + *

+ * + * @see #pauseAll() + */ + @Override + public void resumeAll() throws JobPersistenceException { + + lock(); + try { + jobFacade.clearPausedJobGroups(); + List names = getTriggerGroupNames(); + + for (String name : names) { + resumeTriggers(GroupMatcher.triggerGroupEquals(name)); + } + } finally { + unlock(); + } + } + + boolean applyMisfire(TriggerWrapper tw) throws JobPersistenceException { + long misfireTime = System.currentTimeMillis(); + if (getMisfireThreshold() > 0) { + misfireTime -= getMisfireThreshold(); + } + + Date tnft = tw.getNextFireTime(); + if (tnft == null || tnft.getTime() > misfireTime + || tw.getMisfireInstruction() == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) { return false; } + + Calendar cal = null; + if (tw.getCalendarName() != null) { + cal = retrieveCalendar(tw.getCalendarName()); + } + + signaler.notifyTriggerListenersMisfired(tw.getTriggerClone()); + + tw.updateAfterMisfire(cal, triggerFacade); + + if (tw.getNextFireTime() == null) { + tw.setState(TriggerState.COMPLETE, terracottaClientId, triggerFacade); + signaler.notifySchedulerListenersFinalized(tw.getTriggerClone()); + timeTriggers.remove(tw); + } else if (tnft.equals(tw.getNextFireTime())) { return false; } + + return true; + } + + @Override + public List acquireNextTriggers(long noLaterThan, int maxCount, long timeWindow) + throws JobPersistenceException { + List result = new ArrayList();; + lock(); + try { + for (TriggerWrapper tw : getNextTriggerWrappers(timeTriggers, noLaterThan, maxCount, timeWindow)) { + result.add(markAndCloneTrigger(tw)); + } + return result; + } finally { + try { + unlock(); + } catch (RejoinException e) { + if (!validateAcquired(result)) { + throw e; + } + } + } + } + + private boolean validateAcquired(List result) { + if (result.isEmpty()) { + return false; + } else { + while (!toolkitShutdown) { + try { + lock(); + try { + for (OperableTrigger ot : result) { + TriggerWrapper tw = triggerFacade.get(ot.getKey()); + if (!ot.getFireInstanceId().equals(tw.getTriggerClone().getFireInstanceId()) || !TriggerState.ACQUIRED.equals(tw.getState())) { + return false; + } + } + return true; + } finally { + unlock(); + } + } catch (JobPersistenceException e) { + try { + Thread.sleep(retryInterval); + } catch (InterruptedException f) { + throw new IllegalStateException("Received interrupted exception", f); + } + continue; + } catch (RejoinException e) { + try { + Thread.sleep(retryInterval); + } catch (InterruptedException f) { + throw new IllegalStateException("Received interrupted exception", f); + } + continue; + } + } + throw new IllegalStateException("Scheduler has been shutdown"); + } + } + + OperableTrigger markAndCloneTrigger(final TriggerWrapper tw) { + tw.setState(TriggerState.ACQUIRED, terracottaClientId, triggerFacade); + + String firedInstanceId = terracottaClientId + "-" + String.valueOf(ftrCtr++); + tw.setFireInstanceId(firedInstanceId, triggerFacade); + + return tw.getTriggerClone(); + } + + List getNextTriggerWrappers(final long noLaterThan, final int maxCount, final long timeWindow) + throws JobPersistenceException { + return getNextTriggerWrappers(timeTriggers, noLaterThan, maxCount, timeWindow); + } + + List getNextTriggerWrappers(final TimeTriggerSet source, final long noLaterThan, final int maxCount, + final long timeWindow) throws JobPersistenceException { + + List wrappers = new ArrayList(); + Set acquiredJobKeysForNoConcurrentExec = new HashSet(); + Set excludedTriggers = new HashSet(); + JobPersistenceException caughtJpe = null; + long firstAcquiredTriggerFireTime = 0; + + try { + while (true) { + TriggerWrapper tw = null; + + try { + TriggerKey triggerKey = source.removeFirst(); + if (triggerKey != null) { + tw = triggerFacade.get(triggerKey); + } + if (tw == null) break; + } catch (java.util.NoSuchElementException nsee) { + break; + } + + if (tw.getNextFireTime() == null) { + continue; + } + + // it's possible that we've selected triggers way outside of the max fire ahead time for batches + // (up to idleWaitTime + fireAheadTime) so we need to make sure not to include such triggers. + // So we select from the first next trigger to fire up until the max fire ahead time after that... + // which will perfectly honor the fireAheadTime window because the no firing will occur until + // the first acquired trigger's fire time arrives. + if (firstAcquiredTriggerFireTime > 0 + && tw.getNextFireTime().getTime() > (firstAcquiredTriggerFireTime + timeWindow)) { + source.add(tw); + break; + } + + if (applyMisfire(tw)) { + if (tw.getNextFireTime() != null) { + source.add(tw); + } + continue; + } + + if (tw.getNextFireTime().getTime() > noLaterThan + timeWindow) { + source.add(tw); + break; + } + if (tw.jobDisallowsConcurrence()) { + if (acquiredJobKeysForNoConcurrentExec.contains(tw.getJobKey())) { + excludedTriggers.add(tw); + continue; + } + acquiredJobKeysForNoConcurrentExec.add(tw.getJobKey()); + } + wrappers.add(tw); + if (firstAcquiredTriggerFireTime == 0) firstAcquiredTriggerFireTime = tw.getNextFireTime().getTime(); + if (wrappers.size() == maxCount) { + break; + } + } + } catch (JobPersistenceException jpe) { + caughtJpe = jpe; // hold the exception while we patch back up the collection ... + } + + // If we did excluded triggers to prevent ACQUIRE state due to DisallowConcurrentExecution, we need to add them back + // to store. + if (excludedTriggers.size() > 0) { + for (TriggerWrapper tw : excludedTriggers) { + source.add(tw); + } + } + + // if we held and exception, now we need to put back all the TriggerWrappers that we may have removed from the + // source set + if (caughtJpe != null) { + for (TriggerWrapper tw : wrappers) { + source.add(tw); + } + // and now throw the exception... + throw new JobPersistenceException("Exception encountered while trying to select triggers for firing.", caughtJpe); + } + + return wrappers; + } + + public void setTriggerRemovedFromCandidateFiringListHandler(final TriggerRemovedFromCandidateFiringListHandler triggerRemovedFromCandidateFiringListHandler) { + this.triggerRemovedFromCandidateFiringListHandler = triggerRemovedFromCandidateFiringListHandler; + } + + /** + *

+ * Inform the JobStore that the scheduler no longer plans to fire the given Trigger, that it + * had previously acquired (reserved). + *

+ */ + @Override + public void releaseAcquiredTrigger(OperableTrigger trigger) { + while (!toolkitShutdown) { + try { + lock(); + try { + TriggerWrapper tw = triggerFacade.get(trigger.getKey()); + if (tw != null && trigger.getFireInstanceId().equals(tw.getTriggerClone().getFireInstanceId()) && tw.getState() == TriggerState.ACQUIRED) { + tw.setState(TriggerState.WAITING, terracottaClientId, triggerFacade); + timeTriggers.add(tw); + } + } finally { + unlock(); + } + } catch (RejoinException e) { + try { + Thread.sleep(retryInterval); + } catch (InterruptedException f) { + throw new IllegalStateException("Received interrupted exception", f); + } + continue; + } catch (JobPersistenceException e) { + try { + Thread.sleep(retryInterval); + } catch (InterruptedException f) { + throw new IllegalStateException("Received interrupted exception", f); + } + continue; + } + break; + } + } + + /** + *

+ * Inform the JobStore that the scheduler is now firing the given Trigger (executing its + * associated Job), that it had previously acquired (reserved). + *

+ */ + @Override + public List triggersFired(List triggersFired) throws JobPersistenceException { + + List results = new ArrayList(); + lock(); + try { + for (OperableTrigger trigger : triggersFired) { + TriggerWrapper tw = triggerFacade.get(trigger.getKey()); + // was the trigger deleted since being acquired? + if (tw == null) { + results.add(new TriggerFiredResult((TriggerFiredBundle) null)); + continue; + } + // was the trigger completed, paused, blocked, etc. since being acquired? + if (tw.getState() != TriggerState.ACQUIRED) { + results.add(new TriggerFiredResult((TriggerFiredBundle) null)); + continue; + } + + Calendar cal = null; + if (tw.getCalendarName() != null) { + cal = retrieveCalendar(tw.getCalendarName()); + if (cal == null) { + results.add(new TriggerFiredResult((TriggerFiredBundle) null)); + continue; + } + } + Date prevFireTime = trigger.getPreviousFireTime(); + // in case trigger was replaced between acquiring and firering + timeTriggers.remove(tw); + + // call triggered on our copy, and the scheduler's copy + tw.triggered(cal, triggerFacade); + trigger.triggered(cal); // calendar is already clone()'d so it is okay to pass out to trigger + + // tw.state = EXECUTING; + tw.setState(TriggerState.WAITING, terracottaClientId, triggerFacade); + + TriggerFiredBundle bndle = new TriggerFiredBundle(retrieveJob(trigger.getJobKey()), trigger, cal, false, + new Date(), trigger.getPreviousFireTime(), prevFireTime, + trigger.getNextFireTime()); + + String fireInstanceId = trigger.getFireInstanceId(); + FiredTrigger prev = triggerFacade.getFiredTrigger(fireInstanceId); + triggerFacade.putFiredTrigger(fireInstanceId, new FiredTrigger(terracottaClientId, tw.getKey(), trigger.getPreviousFireTime().getTime())); + getLog().trace("Tracking " + trigger + " has fired on " + fireInstanceId); + if (prev != null) { + // this shouldn't happen + throw new AssertionError("duplicate fireInstanceId detected (" + fireInstanceId + ") for " + trigger + + ", previous is " + prev); + } + + JobDetail job = bndle.getJobDetail(); + + if (job.isConcurrentExectionDisallowed()) { + List trigs = triggerFacade.getTriggerWrappersForJob(job.getKey()); + for (TriggerWrapper ttw : trigs) { + if (ttw.getKey().equals(tw.getKey())) { + continue; + } + if (ttw.getState() == TriggerState.WAITING) { + ttw.setState(TriggerState.BLOCKED, terracottaClientId, triggerFacade); + } + if (ttw.getState() == TriggerState.PAUSED) { + ttw.setState(TriggerState.PAUSED_BLOCKED, terracottaClientId, triggerFacade); + } + timeTriggers.remove(ttw); + if (triggerRemovedFromCandidateFiringListHandler != null) { + triggerRemovedFromCandidateFiringListHandler.removeCandidateTrigger(ttw); + } + } + jobFacade.addBlockedJob(job.getKey()); + } else if (tw.getNextFireTime() != null) { + timeTriggers.add(tw); + } + + results.add(new TriggerFiredResult(bndle)); + } + return results; + } finally { + try { + unlock(); + } catch (RejoinException e) { + if (!validateFiring(results)) { + throw e; + } + } + } + } + + private boolean validateFiring(List result) { + if (result.isEmpty()) { + return false; + } else { + while (!toolkitShutdown) { + try { + lock(); + try { + for (TriggerFiredResult tfr : result) { + TriggerFiredBundle tfb = tfr.getTriggerFiredBundle(); + if (tfb != null && !triggerFacade.containsFiredTrigger(tfb.getTrigger().getFireInstanceId())) { + return false; + } + } + return true; + } finally { + unlock(); + } + } catch (JobPersistenceException e) { + try { + Thread.sleep(retryInterval); + } catch (InterruptedException f) { + throw new IllegalStateException("Received interrupted exception", f); + } + continue; + } catch (RejoinException e) { + try { + Thread.sleep(retryInterval); + } catch (InterruptedException f) { + throw new IllegalStateException("Received interrupted exception", f); + } + continue; + } + } + throw new IllegalStateException("Scheduler has been shutdown"); + } + } + + /** + *

+ * Inform the JobStore that the scheduler has completed the firing of the given Trigger (and + * the execution its associated Job), and that the {@link org.quartz.JobDataMap} in the + * given JobDetail should be updated if the Job is stateful. + *

+ */ + @Override + public void triggeredJobComplete(OperableTrigger trigger, JobDetail jobDetail, + CompletedExecutionInstruction triggerInstCode) { + while (!toolkitShutdown) { + try { + lock(); + try { + String fireId = trigger.getFireInstanceId(); + FiredTrigger removed = triggerFacade.removeFiredTrigger(fireId); + if (removed == null) { + getLog().warn("No fired trigger record found for " + trigger + " (" + fireId + ")"); + break; + } + + JobKey jobKey = jobDetail.getKey(); + JobWrapper jw = jobFacade.get(jobKey); + TriggerWrapper tw = triggerFacade.get(trigger.getKey()); + + // It's possible that the job is null if: + // 1- it was deleted during execution + // 2- RAMJobStore is being used only for volatile jobs / triggers + // from the JDBC job store + if (jw != null) { + if (jw.isPersistJobDataAfterExecution()) { + JobDataMap newData = jobDetail.getJobDataMap(); + if (newData != null) { + newData = (JobDataMap) newData.clone(); + newData.clearDirtyFlag(); + } + jw.setJobDataMap(newData, jobFacade); + } + if (jw.isConcurrentExectionDisallowed()) { + jobFacade.removeBlockedJob(jw.getKey()); + tw.setState(TriggerState.WAITING, terracottaClientId, triggerFacade); + timeTriggers.add(tw); + + List trigs = triggerFacade.getTriggerWrappersForJob(jw.getKey()); + + for (TriggerWrapper ttw : trigs) { + if (ttw.getState() == TriggerState.BLOCKED) { + ttw.setState(TriggerState.WAITING, terracottaClientId, triggerFacade); + timeTriggers.add(ttw); + } + if (ttw.getState() == TriggerState.PAUSED_BLOCKED) { + ttw.setState(TriggerState.PAUSED, terracottaClientId, triggerFacade); + } + } + signaler.signalSchedulingChange(0L); + } + } else { // even if it was deleted, there may be cleanup to do + jobFacade.removeBlockedJob(jobKey); + } + + // check for trigger deleted during execution... + if (tw != null) { + if (triggerInstCode == CompletedExecutionInstruction.DELETE_TRIGGER) { + + if (trigger.getNextFireTime() == null) { + // double check for possible reschedule within job + // execution, which would cancel the need to delete... + if (tw.getNextFireTime() == null) { + removeTrigger(trigger.getKey()); + } + } else { + removeTrigger(trigger.getKey()); + signaler.signalSchedulingChange(0L); + } + } else if (triggerInstCode == CompletedExecutionInstruction.SET_TRIGGER_COMPLETE) { + tw.setState(TriggerState.COMPLETE, terracottaClientId, triggerFacade); + timeTriggers.remove(tw); + signaler.signalSchedulingChange(0L); + } else if (triggerInstCode == CompletedExecutionInstruction.SET_TRIGGER_ERROR) { + getLog().info("Trigger " + trigger.getKey() + " set to ERROR state."); + tw.setState(TriggerState.ERROR, terracottaClientId, triggerFacade); + signaler.signalSchedulingChange(0L); + } else if (triggerInstCode == CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR) { + getLog().info("All triggers of Job " + trigger.getJobKey() + " set to ERROR state."); + setAllTriggersOfJobToState(trigger.getJobKey(), TriggerState.ERROR); + signaler.signalSchedulingChange(0L); + } else if (triggerInstCode == CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_COMPLETE) { + setAllTriggersOfJobToState(trigger.getJobKey(), TriggerState.COMPLETE); + signaler.signalSchedulingChange(0L); + } + } + } finally { + unlock(); + } + } catch (RejoinException e) { + try { + Thread.sleep(retryInterval); + } catch (InterruptedException f) { + throw new IllegalStateException("Received interrupted exception", f); + } + continue; + } catch (JobPersistenceException e) { + try { + Thread.sleep(retryInterval); + } catch (InterruptedException f) { + throw new IllegalStateException("Received interrupted exception", f); + } + continue; + } + break; + } + } + + private void setAllTriggersOfJobToState(JobKey jobKey, TriggerState state) { + List tws = triggerFacade.getTriggerWrappersForJob(jobKey); + + for (TriggerWrapper tw : tws) { + tw.setState(state, terracottaClientId, triggerFacade); + if (state != TriggerState.WAITING) { + timeTriggers.remove(tw); + } + } + } + + /** + * @see org.quartz.spi.JobStore#getPausedTriggerGroups() + */ + @Override + public Set getPausedTriggerGroups() throws JobPersistenceException { + lock(); + try { + return new HashSet(triggerFacade.allPausedTriggersGroupNames()); + } finally { + unlock(); + } + } + + @Override + public void setInstanceId(String schedInstId) { + // + } + + @Override + public void setInstanceName(String schedName) { + // + } + + @Override + public void setTcRetryInterval(long retryInterval) { + this.retryInterval = retryInterval; + } + + public void nodeLeft(ClusterEvent event) { + final String nodeLeft = event.getNode().getId(); + + try { + lock(); + } catch (JobPersistenceException e) { + getLog().info("Job store is already disabled, not processing nodeLeft() for " + nodeLeft); + return; + } + + try { + + List toEval = new ArrayList(); + + for (TriggerKey triggerKey : triggerFacade.allTriggerKeys()) { + TriggerWrapper tw = triggerFacade.get(triggerKey); + String clientId = tw.getLastTerracotaClientId(); + if (clientId != null && clientId.equals(nodeLeft)) { + toEval.add(tw); + } + } + + for (TriggerWrapper tw : toEval) { + evalOrphanedTrigger(tw, false); + } + + for (Iterator iter = triggerFacade.allFiredTriggers().iterator(); iter.hasNext();) { + FiredTrigger ft = iter.next(); + if (nodeLeft.equals(ft.getClientId())) { + getLog().info("Found non-complete fired trigger: " + ft); + iter.remove(); + + TriggerWrapper tw = triggerFacade.get(ft.getTriggerKey()); + if (tw == null) { + getLog().error("no trigger found for executing trigger: " + ft.getTriggerKey()); + continue; + } + + scheduleRecoveryIfNeeded(tw, ft); + } + } + } finally { + unlock(); + } + + // nudge the local scheduler. This is a lazy way to do it. This should perhaps be conditionally happening and + // also passing a real next job time (as opposed to 0) + signaler.signalSchedulingChange(0); + } + + @Override + public long getEstimatedTimeToReleaseAndAcquireTrigger() { + // right now this is a static (but configurable) value. It could be based on actual observation + // of trigger acquire/release at runtime in the future though + return this.estimatedTimeToReleaseAndAcquireTrigger; + } + + @Override + public void setEstimatedTimeToReleaseAndAcquireTrigger(long estimate) { + this.estimatedTimeToReleaseAndAcquireTrigger = estimate; + } + + @Override + public void setThreadPoolSize(final int size) { + // + } + + @Override + public boolean isClustered() { + // We throw an assertion here since this method should never be called directly on this instance. + throw new AssertionError(); + } + + void injectTriggerWrapper(final TriggerWrapper triggerWrapper) { + timeTriggers.add(triggerWrapper); + } + + private static class ShutdownHook implements Runnable { + private final DefaultClusteredJobStore store; + + ShutdownHook(DefaultClusteredJobStore store) { + this.store = store; + } + + @Override + public void run() { + store.disable(); + } + } + + private static class LocalLockState { + private int acquires = 0; + private boolean disabled; + + synchronized void attemptAcquireBegin() throws JobPersistenceException { + if (disabled) { throw new JobPersistenceException("org.terracotta.quartz.TerracottaJobStore is disabled"); } + acquires++; + } + + synchronized void release() { + acquires--; + notifyAll(); + } + + synchronized void disableLocking() throws InterruptedException { + disabled = true; + + while (acquires > 0) { + wait(); + } + } + } + + ClusterInfo getDsoCluster() { + return clusterInfo; + } + + interface TriggerRemovedFromCandidateFiringListHandler { + public boolean removeCandidateTrigger(final TriggerWrapper ttw); + } + + @Override + public void onClusterEvent(ClusterEvent event) { + switch (event.getType()) { + case NODE_JOINED: + case OPERATIONS_DISABLED: + case OPERATIONS_ENABLED: + break; + case NODE_LEFT: + getLog().info("Received node left notification for " + event.getNode().getId()); + nodeLeft(event); + break; + case NODE_REJOINED: + getLog().info("Received rejoin notification " + terracottaClientId + " => " + event.getNode().getId()); + terracottaClientId = event.getNode().getId(); + break; + } + } + + protected TriggerFacade getTriggersFacade() { + return this.triggerFacade; + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/PlainTerracottaJobStore.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/PlainTerracottaJobStore.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/PlainTerracottaJobStore.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,435 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz; + +import org.quartz.Calendar; +import org.quartz.JobDetail; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.quartz.JobKey; +import org.quartz.JobPersistenceException; +import org.quartz.SchedulerConfigException; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.Trigger.CompletedExecutionInstruction; +import org.quartz.TriggerKey; +import org.quartz.impl.matchers.GroupMatcher; +import org.quartz.spi.ClassLoadHelper; +import org.quartz.spi.OperableTrigger; +import org.quartz.spi.SchedulerSignaler; +import org.quartz.spi.TriggerFiredResult; +import org.terracotta.toolkit.cluster.ClusterInfo; +import org.terracotta.toolkit.internal.ToolkitInternal; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Timer; + +public class PlainTerracottaJobStore implements TerracottaJobStoreExtensions { + + private static final long WEEKLY = 7 * 24 * 60 * 60 * 1000L; + private Timer updateCheckTimer; + private volatile T clusteredJobStore = null; + private Long misfireThreshold = null; + private String schedName; + private String synchWrite = "false"; + private Long estimatedTimeToReleaseAndAcquireTrigger = null; + private String schedInstanceId; + private long tcRetryInterval; + private int threadPoolSize; + private final ClusterInfo clusterInfo; + protected final ToolkitInternal toolkit; + + public PlainTerracottaJobStore(ToolkitInternal toolkit) { + this.toolkit = toolkit; + this.clusterInfo = toolkit.getClusterInfo(); + } + + @Override + public void setSynchronousWrite(String synchWrite) { + this.synchWrite = synchWrite; + } + + @Override + public void setThreadPoolSize(final int size) { + this.threadPoolSize = size; + } + + @Override + public List acquireNextTriggers(long noLaterThan, int maxCount, long timeWindow) + throws JobPersistenceException { + return clusteredJobStore.acquireNextTriggers(noLaterThan, maxCount, timeWindow); + } + + @Override + public List getCalendarNames() throws JobPersistenceException { + return clusteredJobStore.getCalendarNames(); + } + + @Override + public List getJobGroupNames() throws JobPersistenceException { + return clusteredJobStore.getJobGroupNames(); + } + + @Override + public Set getJobKeys(final GroupMatcher matcher) throws JobPersistenceException { + return clusteredJobStore.getJobKeys(matcher); + } + + @Override + public int getNumberOfCalendars() throws JobPersistenceException { + return clusteredJobStore.getNumberOfCalendars(); + } + + @Override + public int getNumberOfJobs() throws JobPersistenceException { + return clusteredJobStore.getNumberOfJobs(); + } + + @Override + public int getNumberOfTriggers() throws JobPersistenceException { + return clusteredJobStore.getNumberOfTriggers(); + } + + @Override + public Set getPausedTriggerGroups() throws JobPersistenceException { + return clusteredJobStore.getPausedTriggerGroups(); + } + + @Override + public List getTriggerGroupNames() throws JobPersistenceException { + return clusteredJobStore.getTriggerGroupNames(); + } + + @Override + public Set getTriggerKeys(final GroupMatcher matcher) throws JobPersistenceException { + return clusteredJobStore.getTriggerKeys(matcher); + } + + @Override + public List getTriggersForJob(final JobKey jobKey) throws JobPersistenceException { + return clusteredJobStore.getTriggersForJob(jobKey); + } + + @Override + public Trigger.TriggerState getTriggerState(final TriggerKey triggerKey) throws JobPersistenceException { + return clusteredJobStore.getTriggerState(triggerKey); + } + + @Override + public synchronized void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler) + throws SchedulerConfigException { + if (clusteredJobStore != null) { throw new IllegalStateException("already initialized"); } + + clusteredJobStore = createNewJobStoreInstance(schedName, Boolean.valueOf(synchWrite)); + clusteredJobStore.setThreadPoolSize(threadPoolSize); + + // apply deferred misfire threshold if present + if (misfireThreshold != null) { + clusteredJobStore.setMisfireThreshold(misfireThreshold); + misfireThreshold = null; + } + + if (estimatedTimeToReleaseAndAcquireTrigger != null) { + clusteredJobStore.setEstimatedTimeToReleaseAndAcquireTrigger(estimatedTimeToReleaseAndAcquireTrigger); + estimatedTimeToReleaseAndAcquireTrigger = null; + } + clusteredJobStore.setInstanceId(schedInstanceId); + clusteredJobStore.setTcRetryInterval(tcRetryInterval); + clusteredJobStore.initialize(loadHelper, signaler); + + // update check + scheduleUpdateCheck(); + } + + @Override + public void pauseAll() throws JobPersistenceException { + clusteredJobStore.pauseAll(); + } + + @Override + public void pauseJob(final JobKey jobKey) throws JobPersistenceException { + clusteredJobStore.pauseJob(jobKey); + } + + @Override + public Collection pauseJobs(GroupMatcher matcher) throws JobPersistenceException { + return clusteredJobStore.pauseJobs(matcher); + } + + @Override + public void pauseTrigger(TriggerKey triggerKey) throws JobPersistenceException { + clusteredJobStore.pauseTrigger(triggerKey); + } + + @Override + public Collection pauseTriggers(GroupMatcher matcher) throws JobPersistenceException { + return clusteredJobStore.pauseTriggers(matcher); + } + + @Override + public void releaseAcquiredTrigger(final OperableTrigger trigger) { + clusteredJobStore.releaseAcquiredTrigger(trigger); + } + + @Override + public List triggersFired(final List triggers) throws JobPersistenceException { + return clusteredJobStore.triggersFired(triggers); + } + + @Override + public boolean removeCalendar(String calName) throws JobPersistenceException { + return clusteredJobStore.removeCalendar(calName); + } + + @Override + public boolean removeJob(JobKey jobKey) throws JobPersistenceException { + return clusteredJobStore.removeJob(jobKey); + } + + @Override + public boolean removeTrigger(TriggerKey triggerKey) throws JobPersistenceException { + return clusteredJobStore.removeTrigger(triggerKey); + } + + @Override + public boolean removeJobs(List jobKeys) throws JobPersistenceException { + return clusteredJobStore.removeJobs(jobKeys); + } + + @Override + public boolean removeTriggers(List triggerKeys) throws JobPersistenceException { + return clusteredJobStore.removeTriggers(triggerKeys); + } + + @Override + public void storeJobsAndTriggers(Map> triggersAndJobs, boolean replace) + throws JobPersistenceException { + clusteredJobStore.storeJobsAndTriggers(triggersAndJobs, replace); + } + + @Override + public boolean replaceTrigger(TriggerKey triggerKey, OperableTrigger newTrigger) throws JobPersistenceException { + return clusteredJobStore.replaceTrigger(triggerKey, newTrigger); + } + + @Override + public void resumeAll() throws JobPersistenceException { + clusteredJobStore.resumeAll(); + } + + @Override + public void resumeJob(JobKey jobKey) throws JobPersistenceException { + clusteredJobStore.resumeJob(jobKey); + } + + @Override + public Collection resumeJobs(GroupMatcher matcher) throws JobPersistenceException { + return clusteredJobStore.resumeJobs(matcher); + } + + @Override + public void resumeTrigger(TriggerKey triggerKey) throws JobPersistenceException { + clusteredJobStore.resumeTrigger(triggerKey); + } + + @Override + public Collection resumeTriggers(GroupMatcher matcher) throws JobPersistenceException { + return clusteredJobStore.resumeTriggers(matcher); + } + + @Override + public Calendar retrieveCalendar(String calName) throws JobPersistenceException { + return clusteredJobStore.retrieveCalendar(calName); + } + + @Override + public JobDetail retrieveJob(JobKey jobKey) throws JobPersistenceException { + return clusteredJobStore.retrieveJob(jobKey); + } + + @Override + public OperableTrigger retrieveTrigger(TriggerKey triggerKey) throws JobPersistenceException { + return clusteredJobStore.retrieveTrigger(triggerKey); + } + + @Override + public boolean checkExists(final JobKey jobKey) throws JobPersistenceException { + return clusteredJobStore.checkExists(jobKey); + } + + @Override + public boolean checkExists(final TriggerKey triggerKey) throws JobPersistenceException { + return clusteredJobStore.checkExists(triggerKey); + } + + @Override + public void clearAllSchedulingData() throws JobPersistenceException { + clusteredJobStore.clearAllSchedulingData(); + } + + @Override + public void schedulerStarted() throws SchedulerException { + clusteredJobStore.schedulerStarted(); + } + + @Override + public void schedulerPaused() { + if (clusteredJobStore != null) { + clusteredJobStore.schedulerPaused(); + } + } + + @Override + public void schedulerResumed() { + clusteredJobStore.schedulerResumed(); + } + + @Override + public void shutdown() { + if (clusteredJobStore != null) { + clusteredJobStore.shutdown(); + } + if (updateCheckTimer != null) { + updateCheckTimer.cancel(); + } + } + + @Override + public void storeCalendar(String name, Calendar calendar, boolean replaceExisting, boolean updateTriggers) + throws JobPersistenceException { + clusteredJobStore.storeCalendar(name, calendar, replaceExisting, updateTriggers); + } + + @Override + public void storeJob(JobDetail newJob, boolean replaceExisting) throws JobPersistenceException { + clusteredJobStore.storeJob(newJob, replaceExisting); + } + + @Override + public void storeJobAndTrigger(JobDetail newJob, OperableTrigger newTrigger) throws JobPersistenceException { + clusteredJobStore.storeJobAndTrigger(newJob, newTrigger); + } + + @Override + public void storeTrigger(OperableTrigger newTrigger, boolean replaceExisting) throws JobPersistenceException { + clusteredJobStore.storeTrigger(newTrigger, replaceExisting); + } + + @Override + public boolean supportsPersistence() { + return true; + } + + @Override + public String toString() { + return clusteredJobStore.toString(); + } + + @Override + public void triggeredJobComplete(OperableTrigger trigger, JobDetail jobDetail, + CompletedExecutionInstruction triggerInstCode) { + clusteredJobStore.triggeredJobComplete(trigger, jobDetail, triggerInstCode); + } + + @Override + public synchronized void setMisfireThreshold(long threshold) { + ClusteredJobStore cjs = clusteredJobStore; + if (cjs != null) { + cjs.setMisfireThreshold(threshold); + } else { + misfireThreshold = Long.valueOf(threshold); + } + } + + @Override + public synchronized void setEstimatedTimeToReleaseAndAcquireTrigger(long estimate) { + ClusteredJobStore cjs = clusteredJobStore; + if (cjs != null) { + cjs.setEstimatedTimeToReleaseAndAcquireTrigger(estimate); + } else { + this.estimatedTimeToReleaseAndAcquireTrigger = estimate; + } + } + + @Override + public void setInstanceId(String schedInstId) { + this.schedInstanceId = schedInstId; + } + + @Override + public void setInstanceName(String schedName) { + this.schedName = schedName; + } + + @Override + public void setTcRetryInterval(long retryInterval) { + this.tcRetryInterval = retryInterval; + } + + @Override + public String getUUID() { + return clusterInfo.getCurrentNode().getId(); + } + + protected T createNewJobStoreInstance(String schedulerName, final boolean useSynchWrite) { + return (T) new DefaultClusteredJobStore(useSynchWrite, toolkit, schedulerName); + } + + private void scheduleUpdateCheck() { + if (!Boolean.getBoolean("org.terracotta.quartz.skipUpdateCheck")) { + updateCheckTimer = new Timer("Update Checker", true); + updateCheckTimer.scheduleAtFixedRate(new UpdateChecker(), 100, WEEKLY); + } + } + + @Override + public long getEstimatedTimeToReleaseAndAcquireTrigger() { + return clusteredJobStore.getEstimatedTimeToReleaseAndAcquireTrigger(); + } + + @Override + public boolean isClustered() { + return true; + } + + protected T getClusteredJobStore() { + return clusteredJobStore; + } + + @Override + public String getName() { + return this.getClass().getName(); + } + + @Override + public void jobToBeExecuted(final JobExecutionContext context) { + // + } + + @Override + public void jobExecutionVetoed(final JobExecutionContext context) { + // + } + + @Override + public void jobWasExecuted(final JobExecutionContext context, final JobExecutionException jobException) { + // + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/TerracottaJobStore.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/TerracottaJobStore.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/TerracottaJobStore.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,27 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz; + +import org.terracotta.toolkit.internal.ToolkitInternal; + +public class TerracottaJobStore extends AbstractTerracottaJobStore { + @Override + TerracottaJobStoreExtensions getRealStore(ToolkitInternal toolkit) { + return new PlainTerracottaJobStore(toolkit); + } + +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/TerracottaJobStoreExtensions.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/TerracottaJobStoreExtensions.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/TerracottaJobStoreExtensions.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,39 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz; + +import org.quartz.JobListener; +import org.quartz.spi.JobStore; + +/** + * This interface defines convenience methods on the terracotta job store implementation Without this interface we would + * need to use reflection to invoke these "extra" methods (ie. not present on core JobStore) from the express context + */ +public interface TerracottaJobStoreExtensions extends JobStore, JobListener { + + public void setMisfireThreshold(long threshold); + + public void setEstimatedTimeToReleaseAndAcquireTrigger(long estimate); + + public void setSynchronousWrite(String synchWrite); + + public void setThreadPoolSize(int size); + + public String getUUID(); + + public void setTcRetryInterval(long tcRetryInterval); +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/TerracottaToolkitBuilder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/TerracottaToolkitBuilder.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/TerracottaToolkitBuilder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,163 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz; + +import org.terracotta.toolkit.Toolkit; +import org.terracotta.toolkit.ToolkitFactory; +import org.terracotta.toolkit.ToolkitInstantiationException; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +public class TerracottaToolkitBuilder { + + private static final String TC_TUNNELLED_MBEAN_DOMAIN_KEY = "tunnelledMBeanDomains"; + private static final String TC_CONFIG_SNIPPET_KEY = "tcConfigSnippet"; + private static final String TC_REJOIN_KEY = "rejoin"; + private final TCConfigTypeStatus tcConfigTypeStatus = new TCConfigTypeStatus(); + private final Set tunnelledMBeanDomains = Collections.synchronizedSet(new HashSet()); + + private boolean rejoin = false; + + public Toolkit buildToolkit() throws IllegalStateException { + if (tcConfigTypeStatus.getState() == TCConfigTypeState.INIT) { + // + throw new IllegalStateException( + "Please set the tcConfigSnippet or tcConfigUrl before attempting to create client"); + } + Properties properties = new Properties(); + properties.setProperty(TC_TUNNELLED_MBEAN_DOMAIN_KEY, getTunnelledDomainCSV()); + properties.setProperty(TC_REJOIN_KEY, Boolean.toString(isRejoin())); + switch (tcConfigTypeStatus.getState()) { + case TC_CONFIG_SNIPPET: + properties.setProperty(TC_CONFIG_SNIPPET_KEY, tcConfigTypeStatus.getTcConfigSnippet()); + return createToolkit("toolkit:terracotta:", properties); + case TC_CONFIG_URL: + return createToolkit("toolkit:terracotta://" + tcConfigTypeStatus.getTcConfigUrl(), properties); + default: + throw new IllegalStateException("Unknown tc config type - " + tcConfigTypeStatus.getState()); + } + } + + private Toolkit createToolkit(String toolkitUrl, Properties props) { + try { + return ToolkitFactory.createToolkit(toolkitUrl, props); + } catch (ToolkitInstantiationException e) { + throw new RuntimeException(e); + } + } + + private String getTunnelledDomainCSV() { + StringBuilder sb = new StringBuilder(); + for (String domain : tunnelledMBeanDomains) { + sb.append(domain).append(","); + } + // remove last comma + return sb.deleteCharAt(sb.length() - 1).toString(); + } + + public TerracottaToolkitBuilder addTunnelledMBeanDomain(String tunnelledMBeanDomain) { + this.tunnelledMBeanDomains.add(tunnelledMBeanDomain); + return this; + } + + public Set getTunnelledMBeanDomains() { + return Collections.unmodifiableSet(tunnelledMBeanDomains); + } + + public TerracottaToolkitBuilder removeTunnelledMBeanDomain(String tunnelledMBeanDomain) { + tunnelledMBeanDomains.remove(tunnelledMBeanDomain); + return this; + } + + public TerracottaToolkitBuilder setTCConfigSnippet(String tcConfig) throws IllegalStateException { + tcConfigTypeStatus.setTcConfigSnippet(tcConfig); + return this; + } + + public String getTCConfigSnippet() { + return tcConfigTypeStatus.getTcConfigSnippet(); + } + + public TerracottaToolkitBuilder setTCConfigUrl(String tcConfigUrl) throws IllegalStateException { + tcConfigTypeStatus.setTcConfigUrl(tcConfigUrl); + return this; + } + + public String getTCConfigUrl() { + return tcConfigTypeStatus.getTcConfigUrl(); + } + + public boolean isConfigUrl() { + return tcConfigTypeStatus.getState() == TCConfigTypeState.TC_CONFIG_URL; + } + + public TerracottaToolkitBuilder setRejoin(String rejoin) { + this.rejoin = Boolean.valueOf(rejoin); + return this; + } + + public boolean isRejoin() { + return rejoin; + } + + private static enum TCConfigTypeState { + INIT, TC_CONFIG_SNIPPET, TC_CONFIG_URL; + } + + private static class TCConfigTypeStatus { + private TCConfigTypeState state = TCConfigTypeState.INIT; + + private String tcConfigSnippet; + private String tcConfigUrl; + + public synchronized void setTcConfigSnippet(String tcConfigSnippet) { + if (state == TCConfigTypeState.TC_CONFIG_URL) { + // + throw new IllegalStateException("TCConfig url was already set to - " + tcConfigUrl); + } + this.state = TCConfigTypeState.TC_CONFIG_SNIPPET; + this.tcConfigSnippet = tcConfigSnippet; + } + + public synchronized void setTcConfigUrl(String tcConfigUrl) { + if (state == TCConfigTypeState.TC_CONFIG_SNIPPET) { + // + throw new IllegalStateException("TCConfig snippet was already set to - " + tcConfigSnippet); + } + this.state = TCConfigTypeState.TC_CONFIG_URL; + this.tcConfigUrl = tcConfigUrl; + } + + public synchronized String getTcConfigSnippet() { + return tcConfigSnippet; + } + + public synchronized String getTcConfigUrl() { + return tcConfigUrl; + } + + public TCConfigTypeState getState() { + return state; + } + + } + +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/TransactionControllingLock.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/TransactionControllingLock.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/TransactionControllingLock.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,150 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.terracotta.quartz; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; + +import org.terracotta.toolkit.ToolkitFeatureTypeInternal; +import org.terracotta.toolkit.atomic.ToolkitTransaction; +import org.terracotta.toolkit.atomic.ToolkitTransactionController; +import org.terracotta.toolkit.atomic.ToolkitTransactionType; +import org.terracotta.toolkit.concurrent.locks.ToolkitLock; +import org.terracotta.toolkit.concurrent.locks.ToolkitLockType; +import org.terracotta.toolkit.internal.ToolkitInternal; +import org.terracotta.toolkit.rejoin.RejoinException; + +/** + * + * @author cdennis + */ +class TransactionControllingLock implements ToolkitLock { + + private final ThreadLocal threadState = new ThreadLocal() { + @Override + protected HoldState initialValue() { + return new HoldState(); + } + }; + private final ToolkitTransactionController txnController; + private final ToolkitTransactionType txnType; + private final ToolkitLock delegate; + + public TransactionControllingLock(ToolkitInternal toolkit, ToolkitLock lock, ToolkitTransactionType txnType) { + this.txnController = toolkit.getFeature(ToolkitFeatureTypeInternal.TRANSACTION); + this.txnType = txnType; + this.delegate = lock; + } + + @Override + public Condition newCondition() throws UnsupportedOperationException { + throw new UnsupportedOperationException(); + } + + @Override + public Condition getCondition() { + throw new UnsupportedOperationException(); + } + + @Override + public ToolkitLockType getLockType() { + return delegate.getLockType(); + } + + @Override + public boolean isHeldByCurrentThread() { + return delegate.isHeldByCurrentThread(); + } + + @Override + public void lock() { + delegate.lock(); + try { + threadState.get().lock(); + } catch (RejoinException e) { + delegate.unlock(); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + delegate.lockInterruptibly(); + try { + threadState.get().lock(); + } catch (RejoinException e) { + delegate.unlock(); + } + } + + @Override + public boolean tryLock() { + if (delegate.tryLock()) { + try { + threadState.get().lock(); + } catch (RejoinException e) { + delegate.unlock(); + } + return true; + } else { + return false; + } + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + if (delegate.tryLock(time, unit)) { + try { + threadState.get().lock(); + } catch (RejoinException e) { + delegate.unlock(); + } + return true; + } else { + return false; + } + } + + @Override + public void unlock() { + try { + threadState.get().unlock(); + } finally { + delegate.unlock(); + } + } + + @Override + public String getName() { + return delegate.getName(); + } + + class HoldState { + + private ToolkitTransaction txnHandle; + private int holdCount = 0; + + void lock() { + if (holdCount++ == 0) { + if (txnHandle == null) { + txnHandle = txnController.beginTransaction(txnType); + } else { + throw new AssertionError(); + } + } + } + + void unlock() { + if (--holdCount <= 0) { + try { + txnHandle.commit(); + } catch (RejoinException e) { + throw new RejoinException("Exception caught during commit, transaction may or may not have committed.", e); + } finally { + threadState.remove(); + } + } + } + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/UpdateChecker.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/UpdateChecker.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/UpdateChecker.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,187 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz; + + +import org.quartz.core.QuartzScheduler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.Properties; +import java.util.TimerTask; + +/** + * Check for updates and alert users if an update is available + * + * @author Hung Huynh + */ +public class UpdateChecker extends TimerTask { + private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class); + private static final long MILLIS_PER_SECOND = 1000L; + private static final String UNKNOWN = "UNKNOWN"; + private static final String UPDATE_CHECK_URL = "http://www.terracotta.org/kit/reflector?kitID=quartz&pageID=update.properties"; + private static final long START_TIME = System.currentTimeMillis(); + private static final String PRODUCT_NAME = "Quartz Terracotta"; + + /** + * Run the update check + */ + @Override + public void run() { + checkForUpdate(); + } + + /** + * This method ensures that there will be no exception thrown. + */ + public void checkForUpdate() { + try { + if (!Boolean.getBoolean("org.terracotta.quartz.skipUpdateCheck")) { + doCheck(); + } + } catch (Throwable t) { + LOG.debug("Update check failed: " + t.toString()); + } + } + + private void doCheck() throws IOException { + LOG.debug("Checking for update..."); + URL updateUrl = buildUpdateCheckUrl(); + Properties updateProps = getUpdateProperties(updateUrl); + String currentVersion = getQuartzVersion(); + String propVal = updateProps.getProperty("general.notice"); + if (notBlank(propVal)) { + LOG.info(propVal); + } + propVal = updateProps.getProperty(currentVersion + ".notices"); + if (notBlank(propVal)) { + LOG.info(propVal); + } + propVal = updateProps.getProperty(currentVersion + ".updates"); + if (notBlank(propVal)) { + StringBuilder sb = new StringBuilder(); + String[] newVersions = propVal.split(","); + for (int i = 0; i < newVersions.length; i++) { + String newVersion = newVersions[i].trim(); + if (i > 0) { + sb.append(", "); + } + sb.append(newVersion); + propVal = updateProps.getProperty(newVersion + ".release-notes"); + if (notBlank(propVal)) { + sb.append(" ["); + sb.append(propVal); + sb.append("]"); + } + } + if (sb.length() > 0) { + LOG.info("New update(s) found: " + sb.toString()); + } + } else { + // Do nothing at all (ever) on no updates found (DEV-3799) + } + } + + private String getQuartzVersion() { + return String.format("%s.%s.%s", QuartzScheduler.getVersionMajor(), QuartzScheduler.getVersionMinor(), + QuartzScheduler.getVersionIteration()); + } + + private Properties getUpdateProperties(URL updateUrl) throws IOException { + URLConnection connection = updateUrl.openConnection(); + InputStream in = connection.getInputStream(); + try { + Properties props = new Properties(); + props.load(connection.getInputStream()); + return props; + } finally { + if (in != null) { + in.close(); + } + } + } + + private URL buildUpdateCheckUrl() throws MalformedURLException, UnsupportedEncodingException { + String url = System.getProperty("quartz.update-check.url", UPDATE_CHECK_URL); + String connector = url.indexOf('?') > 0 ? "&" : "?"; + return new URL(url + connector + buildParamsString()); + } + + private String buildParamsString() throws UnsupportedEncodingException { + StringBuilder sb = new StringBuilder(); + sb.append("id="); + sb.append(getClientId()); + sb.append("&os-name="); + sb.append(urlEncode(getProperty("os.name"))); + sb.append("&jvm-name="); + sb.append(urlEncode(getProperty("java.vm.name"))); + sb.append("&jvm-version="); + sb.append(urlEncode(getProperty("java.version"))); + sb.append("&platform="); + sb.append(urlEncode(getProperty("os.arch"))); + sb.append("&tc-version="); + sb.append(urlEncode(getQuartzVersion())); + sb.append("&tc-product="); + sb.append(urlEncode(PRODUCT_NAME)); + sb.append("&source="); + sb.append(urlEncode(PRODUCT_NAME)); + sb.append("&uptime-secs="); + sb.append(getUptimeInSeconds()); + sb.append("&patch="); + sb.append(urlEncode(UNKNOWN)); + return sb.toString(); + } + + private long getUptimeInSeconds() { + long uptime = System.currentTimeMillis() - START_TIME; + return uptime > 0 ? (uptime / MILLIS_PER_SECOND) : 0; + } + + private int getClientId() { + try { + return InetAddress.getLocalHost().hashCode(); + } catch (Throwable t) { + return 0; + } + } + + private String urlEncode(String param) throws UnsupportedEncodingException { + return URLEncoder.encode(param, "UTF-8"); + } + + private String getProperty(String prop) { + return System.getProperty(prop, UNKNOWN); + } + + private boolean notBlank(String s) { + return s != null && s.trim().length() > 0; + } + + public static void main(String[] args) { + new UpdateChecker().run(); + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/collections/SerializationHelper.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/collections/SerializationHelper.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/collections/SerializationHelper.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,159 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + */ +package org.terracotta.quartz.collections; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; + +public class SerializationHelper { + /** + * String keys which are really serialized objects will have this as their first char This particular value was chosen + * since it is an invalid character in UTF-16 (http://unicode.org/faq/utf_bom.html#utf16-7) + */ + private static final char MARKER = 0xFFFE; + + public static byte[] serialize(Object obj) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + oos.close(); + return baos.toByteArray(); + } catch (IOException e) { + throw new RuntimeException("error serializing " + obj, e); + } + } + + public static Object deserialize(byte[] bytes) { + try { + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bais); + Object obj = ois.readObject(); + ois.close(); + return obj; + } catch (Exception e) { + throw new RuntimeException("error deserializing " + bytes, e); + } + } + + public static Object deserializeFromString(String key) throws IOException, ClassNotFoundException { + if (key.length() >= 1 && key.charAt(0) == MARKER) { + ObjectInputStream ois = new ObjectInputStream(new StringSerializedObjectInputStream(key)); + return ois.readObject(); + } + + return key; + } + + public static String serializeToString(Object key) throws IOException { + if (key instanceof String) { + String stringKey = (String) key; + + // disallow Strings that start with our marker + if (stringKey.length() >= 1) { + if (stringKey.charAt(0) == MARKER) { + // + throw new IOException("Illegal string key: " + stringKey); + } + + } + + return stringKey; + } + + StringSerializedObjectOutputStream out = new StringSerializedObjectOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(out); + writeStringKey(key, oos); + oos.close(); + + return out.toString(); + } + + private static void writeStringKey(final Object key, final ObjectOutputStream oos) throws IOException { + oos.writeObject(key); + } + + private static class StringSerializedObjectOutputStream extends OutputStream { + private int count; + private char[] buf; + + StringSerializedObjectOutputStream() { + this(16); + } + + StringSerializedObjectOutputStream(int size) { + size = Math.max(1, size); + buf = new char[size]; + + // always add our marker char + buf[count++] = MARKER; + } + + @Override + public void write(int b) { + if (count + 1 > buf.length) { + char[] newbuf = new char[buf.length << 1]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + + writeChar(b); + } + + private void writeChar(int b) { + // hibyte is always zero since UTF-8 encoding used for String storage in DSO! + buf[count++] = (char) (b & 0xFF); + } + + @Override + public void write(byte[] b, int off, int len) { + if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { return; } + int newcount = count + len; + if (newcount > buf.length) { + char newbuf[] = new char[Math.max(buf.length << 1, newcount)]; + System.arraycopy(buf, 0, newbuf, 0, count); + buf = newbuf; + } + + for (int i = 0; i < len; i++) { + writeChar(b[off + i]); + } + } + + @Override + public String toString() { + return new String(buf, 0, count); + } + } + + private static class StringSerializedObjectInputStream extends InputStream { + private final String source; + private final int length; + private int index; + + StringSerializedObjectInputStream(String source) { + this.source = source; + this.length = source.length(); + + read(); // skip marker char + } + + @Override + public int read() { + if (index == length) { + // EOF + return -1; + } + + return source.charAt(index++) & 0xFF; + } + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/collections/SerializedToolkitStore.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/collections/SerializedToolkitStore.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/collections/SerializedToolkitStore.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,437 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. 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 org.terracotta.quartz.collections; + +import org.terracotta.toolkit.store.ToolkitStore; +import org.terracotta.toolkit.concurrent.locks.ToolkitReadWriteLock; +import org.terracotta.toolkit.config.Configuration; +import org.terracotta.toolkit.search.QueryBuilder; +import org.terracotta.toolkit.search.attribute.ToolkitAttributeExtractor; + +import java.io.IOException; +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +public class SerializedToolkitStore implements ToolkitStore { + private final ToolkitStore toolkitStore; + + public SerializedToolkitStore(ToolkitStore toolkitMap) { + this.toolkitStore = toolkitMap; + } + + @Override + public int size() { + return this.toolkitStore.size(); + } + + @Override + public boolean isEmpty() { + return this.toolkitStore.isEmpty(); + } + + private static String serializeToString(Object key) { + try { + return SerializationHelper.serializeToString(key); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static Object deserializeFromString(String key) { + try { + return SerializationHelper.deserializeFromString(key); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean containsKey(Object key) { + return this.toolkitStore.containsKey(serializeToString(key)); + } + + @Override + public V get(Object key) { + return this.toolkitStore.get(serializeToString(key)); + } + + @Override + public V put(K key, V value) { + return this.toolkitStore.put(serializeToString(key), value); + } + + @Override + public V remove(Object key) { + return this.toolkitStore.remove(serializeToString(key)); + } + + @Override + public void putAll(Map m) { + Map tempMap = new HashMap(); + for (Entry entry : m.entrySet()) { + tempMap.put(serializeToString(entry.getKey()), entry.getValue()); + } + + toolkitStore.putAll(tempMap); + } + + @Override + public void clear() { + toolkitStore.clear(); + } + + @Override + public Set keySet() { + return new ToolkitKeySet(toolkitStore.keySet()); + } + + @Override + public boolean isDestroyed() { + return toolkitStore.isDestroyed(); + } + + @Override + public void destroy() { + toolkitStore.destroy(); + } + + @Override + public String getName() { + return toolkitStore.getName(); + } + + @Override + public ToolkitReadWriteLock createLockForKey(K key) { + return toolkitStore.createLockForKey(serializeToString(key)); + } + + @Override + public void removeNoReturn(Object key) { + toolkitStore.removeNoReturn(serializeToString(key)); + } + + @Override + public void putNoReturn(K key, V value) { + toolkitStore.putNoReturn(serializeToString(key), value); + } + + @Override + public Map getAll(Collection keys) { + HashSet tempSet = new HashSet(); + for (K key : keys) { + tempSet.add(serializeToString(key)); + } + + Map m = toolkitStore.getAll(tempSet); + Map tempMap = m.isEmpty() ? Collections.EMPTY_MAP : new HashMap(); + + for (Entry entry : m.entrySet()) { + tempMap.put((K) deserializeFromString(entry.getKey()), entry.getValue()); + } + + return tempMap; + } + + @Override + public Configuration getConfiguration() { + return toolkitStore.getConfiguration(); + } + + @Override + public void setConfigField(String name, Serializable value) { + toolkitStore.setConfigField(name, value); + } + + @Override + public boolean containsValue(Object value) { + return toolkitStore.containsValue(value); + } + + @Override + public V putIfAbsent(K key, V value) { + return toolkitStore.putIfAbsent(serializeToString(key), value); + } + + @Override + public Set> entrySet() { + return new ToolkitEntrySet(this.toolkitStore.entrySet()); + } + + @Override + public Collection values() { + return this.toolkitStore.values(); + } + + @Override + public boolean remove(Object key, Object value) { + return this.toolkitStore.remove(serializeToString(key), value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + return this.toolkitStore.replace(serializeToString(key), oldValue, newValue); + } + + @Override + public V replace(K key, V value) { + return this.toolkitStore.replace(serializeToString(key), value); + } + + private static class ToolkitEntrySet implements Set> { + private final Set> set; + + public ToolkitEntrySet(Set> set) { + this.set = set; + } + + @Override + public int size() { + return set.size(); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + if (!(o instanceof Map.Entry)) { return false; } + + Map.Entry entry = (java.util.Map.Entry) o; + ToolkitMapEntry toolkitEntry = null; + toolkitEntry = new ToolkitMapEntry(serializeToString(entry.getKey()), entry.getValue()); + return this.set.contains(toolkitEntry); + } + + @Override + public Iterator> iterator() { + return new ToolkitEntryIterator(set.iterator()); + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public T[] toArray(T[] a) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean add(java.util.Map.Entry e) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection> c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + } + + private static class ToolkitEntryIterator implements Iterator> { + + private final Iterator> iter; + + public ToolkitEntryIterator(Iterator> iter) { + this.iter = iter; + } + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public java.util.Map.Entry next() { + Map.Entry entry = iter.next(); + if (entry == null) { return null; } + return new ToolkitMapEntry(deserializeFromString(entry.getKey()), entry.getValue()); + } + + @Override + public void remove() { + iter.remove(); + } + + } + + private static class ToolkitMapEntry implements Map.Entry { + private final K k; + private final V v; + + public ToolkitMapEntry(K k, V v) { + this.k = k; + this.v = v; + } + + @Override + public K getKey() { + return k; + } + + @Override + public V getValue() { + return v; + } + + @Override + public V setValue(V value) { + throw new UnsupportedOperationException(); + } + + } + + private static class ToolkitKeySet implements Set { + + private final Set set; + + public ToolkitKeySet(Set set) { + this.set = set; + } + + @Override + public int size() { + return set.size(); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return set.contains(serializeToString(o)); + } + + @Override + public Iterator iterator() { + return new ToolkitKeyIterator(set.iterator()); + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public T[] toArray(T[] a) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean add(K e) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + } + + private static class ToolkitKeyIterator implements Iterator { + + private final Iterator iter; + + public ToolkitKeyIterator(Iterator iter) { + this.iter = iter; + } + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + + @Override + public K next() { + String k = iter.next(); + if (k == null) { return null; } + return (K) deserializeFromString(k); + } + + @Override + public void remove() { + iter.remove(); + } + + } + + @Override + public void setAttributeExtractor(ToolkitAttributeExtractor attrExtractor) { + this.toolkitStore.setAttributeExtractor(attrExtractor); + } + + @Override + public QueryBuilder createQueryBuilder() { + throw new UnsupportedOperationException(); + } + +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/collections/TimeTrigger.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/collections/TimeTrigger.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/collections/TimeTrigger.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,63 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + */ +package org.terracotta.quartz.collections; + +import org.quartz.Trigger; +import org.quartz.TriggerKey; + +import java.io.Serializable; +import java.util.Date; + +public class TimeTrigger implements Comparable, Serializable { + + private final TriggerKey triggerKey; + private final Long nextFireTime; + private final int priority; + + TimeTrigger(TriggerKey triggerKey, Date next, int priority) { + this.triggerKey = triggerKey; + this.nextFireTime = next == null ? null : next.getTime(); + this.priority = priority; + } + + TriggerKey getTriggerKey() { + return triggerKey; + } + + int getPriority() { + return priority; + } + + Date getNextFireTime() { + return nextFireTime == null ? null : new Date(nextFireTime); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TimeTrigger) { + TimeTrigger other = (TimeTrigger) obj; + return triggerKey.equals(other.triggerKey); + } + return false; + } + + @Override + public int hashCode() { + return triggerKey.hashCode(); + } + + @Override + public String toString() { + return "TimeTrigger [triggerKey=" + triggerKey + ", nextFireTime=" + new Date(nextFireTime) + ", priority=" + + priority + "]"; + } + + @Override + public int compareTo(TimeTrigger tt2) { + TimeTrigger tt1 = this; + return Trigger.TriggerTimeComparator.compare(tt1.getNextFireTime(), tt1.getPriority(), tt1.getTriggerKey(), + tt2.getNextFireTime(), tt2.getPriority(), tt2.getTriggerKey()); + } + +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/terracotta/quartz/collections/TimeTriggerSet.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/collections/TimeTriggerSet.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/collections/TimeTriggerSet.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,65 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz.collections; + +import org.quartz.TriggerKey; +import org.terracotta.quartz.wrappers.TriggerWrapper; +import org.terracotta.toolkit.collections.ToolkitSortedSet; + +import java.util.Iterator; + +public class TimeTriggerSet { + private final ToolkitSortedSet timeTriggers; + + public TimeTriggerSet(ToolkitSortedSet timeTriggers) { + this.timeTriggers = timeTriggers; + } + + public boolean add(TriggerWrapper wrapper) { + TimeTrigger timeTrigger = new TimeTrigger(wrapper.getKey(), wrapper.getNextFireTime(), wrapper.getPriority()); + return timeTriggers.add(timeTrigger); + } + + public boolean remove(TriggerWrapper wrapper) { + TimeTrigger timeTrigger = new TimeTrigger(wrapper.getKey(), wrapper.getNextFireTime(), wrapper.getPriority()); + boolean result = timeTriggers.remove(timeTrigger); + return result; + } + + public TriggerKey removeFirst() { + Iterator iter = timeTriggers.iterator(); + TimeTrigger tt = null; + if (iter.hasNext()) { + tt = iter.next(); + iter.remove(); + } + return tt == null ? null : tt.getTriggerKey(); + } + + public boolean isDestroyed() { + return timeTriggers.isDestroyed(); + } + + public void destroy() { + timeTriggers.destroy(); + } + + public int size() { + return timeTriggers.size(); + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/collections/ToolkitDSHolder.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/collections/ToolkitDSHolder.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/collections/ToolkitDSHolder.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,231 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz.collections; + +import java.util.HashMap; +import java.util.Map; +import org.quartz.Calendar; +import org.quartz.JobKey; +import org.quartz.TriggerKey; +import org.terracotta.quartz.wrappers.FiredTrigger; +import org.terracotta.quartz.wrappers.JobWrapper; +import org.terracotta.quartz.wrappers.TriggerWrapper; +import org.terracotta.toolkit.Toolkit; +import org.terracotta.toolkit.concurrent.locks.ToolkitLock; +import org.terracotta.toolkit.internal.ToolkitInternal; +import org.terracotta.toolkit.internal.concurrent.locks.ToolkitLockTypeInternal; +import org.terracotta.toolkit.store.ToolkitConfigFields.Consistency; + +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import org.terracotta.toolkit.builder.ToolkitStoreConfigBuilder; +import org.terracotta.toolkit.collections.ToolkitSet; +import org.terracotta.toolkit.store.ToolkitConfigFields; +import org.terracotta.toolkit.store.ToolkitStore; + +/** + * How JOBS mappings will look?
+ * JobKey(name, groupname) -> JobWrapper
+ * groupName -> List
+ * List -> allGroupNames
+ */ +public class ToolkitDSHolder { + private static final String JOBS_MAP_PREFIX = "_tc_quartz_jobs"; + private static final String ALL_JOBS_GROUP_NAMES_SET_PREFIX = "_tc_quartz_grp_names"; + private static final String PAUSED_GROUPS_SET_PREFIX = "_tc_quartz_grp_paused_names"; + private static final String BLOCKED_JOBS_SET_PREFIX = "_tc_quartz_blocked_jobs"; + private static final String JOBS_GROUP_MAP_PREFIX = "_tc_quartz_grp_jobs_"; + + private static final String TRIGGERS_MAP_PREFIX = "_tc_quartz_triggers"; + private static final String TRIGGERS_GROUP_MAP_PREFIX = "_tc_quartz_grp_triggers_"; + private static final String ALL_TRIGGERS_GROUP_NAMES_SET_PREFIX = "_tc_quartz_grp_names_triggers"; + private static final String PAUSED_TRIGGER_GROUPS_SET_PREFIX = "_tc_quartz_grp_paused_trogger_names"; + private static final String TIME_TRIGGER_SORTED_SET_PREFIX = "_tc_time_trigger_sorted_set"; + private static final String FIRED_TRIGGER_MAP_PREFIX = "_tc_quartz_fired_trigger"; + private static final String CALENDAR_WRAPPER_MAP_PREFIX = "_tc_quartz_calendar_wrapper"; + private static final String SINGLE_LOCK_NAME_PREFIX = "_tc_quartz_single_lock"; + + private static final String DELIMETER = "|"; + private final String jobStoreName; + protected final Toolkit toolkit; + + private final AtomicReference> jobsMapReference = new AtomicReference>(); + private final AtomicReference> triggersMapReference = new AtomicReference>(); + + private final AtomicReference> allGroupsReference = new AtomicReference>(); + private final AtomicReference> allTriggersGroupsReference = new AtomicReference>(); + private final AtomicReference> pausedGroupsReference = new AtomicReference>(); + private final AtomicReference> blockedJobsReference = new AtomicReference>(); + private final Map> jobsGroupSet = new HashMap>(); + private final Map> triggersGroupSet = new HashMap>(); + private final AtomicReference> pausedTriggerGroupsReference = new AtomicReference>(); + + private final AtomicReference> firedTriggersMapReference = new AtomicReference>(); + private final AtomicReference> calendarWrapperMapReference = new AtomicReference>(); + private final AtomicReference timeTriggerSetReference = new AtomicReference(); + + private final Map> toolkitMaps = new HashMap>(); + + public ToolkitDSHolder(String jobStoreName, Toolkit toolkit) { + this.jobStoreName = jobStoreName; + this.toolkit = toolkit; + } + + protected final String generateName(String prefix) { + return prefix + DELIMETER + jobStoreName; + } + + public SerializedToolkitStore getOrCreateJobsMap() { + String jobsMapName = generateName(JOBS_MAP_PREFIX); + SerializedToolkitStore temp = new SerializedToolkitStore(createStore(jobsMapName)); + jobsMapReference.compareAndSet(null, temp); + return jobsMapReference.get(); + } + + protected ToolkitStore toolkitMap(String nameOfMap) { + ToolkitStore map = toolkitMaps.get(nameOfMap); + if (map != null && !map.isDestroyed()) { + return map; + } else { + map = createStore(nameOfMap); + toolkitMaps.put(nameOfMap, map); + return map; + } + } + + private ToolkitStore createStore(String nameOfMap) { + ToolkitStoreConfigBuilder builder = new ToolkitStoreConfigBuilder(); + return toolkit.getStore(nameOfMap, builder.consistency(Consistency.STRONG).concurrency(1).build(), null); + } + + public SerializedToolkitStore getOrCreateTriggersMap() { + String triggersMapName = generateName(TRIGGERS_MAP_PREFIX); + SerializedToolkitStore temp = new SerializedToolkitStore( + createStore(triggersMapName)); + triggersMapReference.compareAndSet(null, temp); + return triggersMapReference.get(); + } + + public ToolkitStore getOrCreateFiredTriggersMap() { + String firedTriggerMapName = generateName(FIRED_TRIGGER_MAP_PREFIX); + ToolkitStore temp = createStore(firedTriggerMapName); + firedTriggersMapReference.compareAndSet(null, temp); + return firedTriggersMapReference.get(); + } + + public ToolkitStore getOrCreateCalendarWrapperMap() { + String calendarWrapperName = generateName(CALENDAR_WRAPPER_MAP_PREFIX); + ToolkitStore temp = createStore(calendarWrapperName); + calendarWrapperMapReference.compareAndSet(null, temp); + return calendarWrapperMapReference.get(); + } + + public Set getOrCreateAllGroupsSet() { + String allGrpSetNames = generateName(ALL_JOBS_GROUP_NAMES_SET_PREFIX); + ToolkitSet temp = toolkit.getSet(allGrpSetNames, String.class); + allGroupsReference.compareAndSet(null, temp); + + return allGroupsReference.get(); + } + + public Set getOrCreateBlockedJobsSet() { + String blockedJobsSetName = generateName(BLOCKED_JOBS_SET_PREFIX); + ToolkitSet temp = toolkit.getSet(blockedJobsSetName, JobKey.class); + blockedJobsReference.compareAndSet(null, temp); + + return blockedJobsReference.get(); + } + + public Set getOrCreatePausedGroupsSet() { + String pausedGrpsSetName = generateName(PAUSED_GROUPS_SET_PREFIX); + ToolkitSet temp = toolkit.getSet(pausedGrpsSetName, String.class); + pausedGroupsReference.compareAndSet(null, temp); + + return pausedGroupsReference.get(); + } + + public Set getOrCreatePausedTriggerGroupsSet() { + String pausedGrpsSetName = generateName(PAUSED_TRIGGER_GROUPS_SET_PREFIX); + ToolkitSet temp = toolkit.getSet(pausedGrpsSetName, String.class); + pausedTriggerGroupsReference.compareAndSet(null, temp); + + return pausedTriggerGroupsReference.get(); + } + + public Set getOrCreateJobsGroupMap(String name) { + ToolkitSet set = jobsGroupSet.get(name); + + if (set != null && !set.isDestroyed()) { + return set; + } else { + String nameForMap = generateName(JOBS_GROUP_MAP_PREFIX + name); + set = toolkit.getSet(nameForMap, String.class); + jobsGroupSet.put(name, set); + return set; + } + } + + public void removeJobsGroupMap(String name) { + ToolkitSet set = jobsGroupSet.remove(name); + if (set != null) { + set.destroy(); + } + } + + public Set getOrCreateTriggersGroupMap(String name) { + ToolkitSet set = triggersGroupSet.get(name); + + if (set != null && !set.isDestroyed()) { + return set; + } else { + String nameForMap = generateName(TRIGGERS_GROUP_MAP_PREFIX + name); + set = toolkit.getSet(nameForMap, String.class); + triggersGroupSet.put(name, set); + return set; + } + } + + public void removeTriggersGroupMap(String name) { + ToolkitSet set = triggersGroupSet.remove(name); + if (set != null) { + set.destroy(); + } + } + + public Set getOrCreateAllTriggersGroupsSet() { + String allTriggersGrpsName = generateName(ALL_TRIGGERS_GROUP_NAMES_SET_PREFIX); + ToolkitSet temp = toolkit.getSet(allTriggersGrpsName, String.class); + allTriggersGroupsReference.compareAndSet(null, temp); + + return allTriggersGroupsReference.get(); + } + + public TimeTriggerSet getOrCreateTimeTriggerSet() { + String triggerSetName = generateName(TIME_TRIGGER_SORTED_SET_PREFIX); + TimeTriggerSet set = new TimeTriggerSet(toolkit.getSortedSet(triggerSetName, TimeTrigger.class)); + timeTriggerSetReference.compareAndSet(null, set); + + return timeTriggerSetReference.get(); + } + + public ToolkitLock getLock(ToolkitLockTypeInternal lockType) { + String lockName = generateName(SINGLE_LOCK_NAME_PREFIX); + return ((ToolkitInternal) toolkit).getLock(lockName, lockType); + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/DefaultWrapperFactory.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/DefaultWrapperFactory.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/DefaultWrapperFactory.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,35 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz.wrappers; + +import org.quartz.JobDetail; +import org.quartz.spi.OperableTrigger; + +public class DefaultWrapperFactory implements WrapperFactory { + + @Override + public JobWrapper createJobWrapper(JobDetail jobDetail) { + return new JobWrapper(jobDetail); + } + + @Override + public TriggerWrapper createTriggerWrapper(OperableTrigger trigger, boolean jobDisallowsConcurrence) { + return new TriggerWrapper(trigger, jobDisallowsConcurrence); + } + +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/FiredTrigger.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/FiredTrigger.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/FiredTrigger.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,86 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz.wrappers; + +import org.quartz.TriggerKey; + +import java.io.Serializable; +import java.util.Date; + +public class FiredTrigger implements Serializable { + private final String clientId; + private final TriggerKey triggerKey; + private final long scheduledFireTime; + private final long fireTime; + + public FiredTrigger(String clientId, TriggerKey triggerKey, long scheduledFireTime) { + this.clientId = clientId; + this.triggerKey = triggerKey; + this.scheduledFireTime = scheduledFireTime; + this.fireTime = System.currentTimeMillis(); + } + + public String getClientId() { + return clientId; + } + + public TriggerKey getTriggerKey() { + return triggerKey; + } + + public long getScheduledFireTime() { + return scheduledFireTime; + } + + public long getFireTime() { + return fireTime; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + triggerKey + ", " + getClientId() + ", " + new Date(fireTime) + ")"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((clientId == null) ? 0 : clientId.hashCode()); + result = prime * result + (int) (scheduledFireTime ^ (scheduledFireTime >>> 32)); + result = prime * result + (int) (fireTime ^ (fireTime >>> 32)); + result = prime * result + ((triggerKey == null) ? 0 : triggerKey.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + FiredTrigger other = (FiredTrigger) obj; + if (clientId == null) { + if (other.clientId != null) return false; + } else if (!clientId.equals(other.clientId)) return false; + if (scheduledFireTime != other.scheduledFireTime) return false; + if (fireTime != other.fireTime) return false; + if (triggerKey == null) { + if (other.triggerKey != null) return false; + } else if (!triggerKey.equals(other.triggerKey)) return false; + return true; + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/JobFacade.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/JobFacade.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/JobFacade.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,102 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz.wrappers; + +import org.quartz.JobKey; +import org.terracotta.quartz.collections.ToolkitDSHolder; +import org.terracotta.toolkit.store.ToolkitStore; + +import java.util.Set; + +public class JobFacade { + private final ToolkitStore jobsByFQN; + private final Set allJobsGroupNames; + private final Set pausedJobGroups; + private final Set blockedJobs; + + public JobFacade(ToolkitDSHolder toolkitDSHolder) { + this.jobsByFQN = toolkitDSHolder.getOrCreateJobsMap(); + this.allJobsGroupNames = toolkitDSHolder.getOrCreateAllGroupsSet(); + this.pausedJobGroups = toolkitDSHolder.getOrCreatePausedGroupsSet(); + this.blockedJobs = toolkitDSHolder.getOrCreateBlockedJobsSet(); + } + + public JobWrapper get(JobKey jobKey) { + return jobsByFQN.get(jobKey); + } + + public void put(JobKey jobKey, JobWrapper jobWrapper) { + jobsByFQN.putNoReturn(jobKey, jobWrapper); + } + + public boolean containsKey(JobKey key) { + return jobsByFQN.containsKey(key); + } + + public boolean hasGroup(String name) { + return allJobsGroupNames.contains(name); + } + + public boolean addGroup(String name) { + return allJobsGroupNames.add(name); + } + + public boolean addPausedGroup(String name) { + return pausedJobGroups.add(name); + } + + public JobWrapper remove(JobKey jobKey) { + return jobsByFQN.remove(jobKey); + } + + public boolean removeGroup(String group) { + return allJobsGroupNames.remove(group); + } + + public boolean pausedGroupsContain(String group) { + return pausedJobGroups.contains(group); + } + + public boolean blockedJobsContain(JobKey jobKey) { + return blockedJobs.contains(jobKey); + } + + public int numberOfJobs() { + return jobsByFQN.size(); + } + + public Set getAllGroupNames() { + return allJobsGroupNames; + } + + public boolean removePausedJobGroup(String group) { + return pausedJobGroups.remove(group); + } + + public void clearPausedJobGroups() { + pausedJobGroups.clear(); + } + + public void addBlockedJob(JobKey key) { + blockedJobs.add(key); + } + + public boolean removeBlockedJob(JobKey key) { + return blockedJobs.remove(key); + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/JobWrapper.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/JobWrapper.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/JobWrapper.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,89 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz.wrappers; + +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.JobKey; + +import java.io.Serializable; + +public class JobWrapper implements Serializable { + protected JobDetail jobDetail; + + protected JobWrapper(JobDetail jobDetail) { + this.jobDetail = jobDetail; + } + + public JobKey getKey() { + return this.jobDetail.getKey(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof JobWrapper) { + JobWrapper jw = (JobWrapper) obj; + if (jw.jobDetail.getKey().equals(this.jobDetail.getKey())) { return true; } + } + + return false; + } + + @Override + public int hashCode() { + return jobDetail.getKey().hashCode(); + } + + @Override + public String toString() { + return "[" + jobDetail.toString() + "]"; + } + + public boolean requestsRecovery() { + return jobDetail.requestsRecovery(); + } + + public boolean isPersistJobDataAfterExecution() { + return jobDetail.isPersistJobDataAfterExecution(); + } + + public boolean isConcurrentExectionDisallowed() { + return jobDetail.isConcurrentExectionDisallowed(); + } + + public boolean isDurable() { + return jobDetail.isDurable(); + } + + public JobDetail getJobDetailClone() { + return (JobDetail) jobDetail.clone(); + } + + public void setJobDataMap(JobDataMap newData, JobFacade jobFacade) { + jobDetail = jobDetail.getJobBuilder().setJobData(newData).build(); + jobFacade.put(jobDetail.getKey(), this); + } + + public JobDataMap getJobDataMapClone() { + return (JobDataMap) jobDetail.getJobDataMap().clone(); + } + + public boolean isInstanceof(Class clazz) { + return clazz.isInstance(jobDetail); + } +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/TriggerFacade.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/TriggerFacade.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/TriggerFacade.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,149 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz.wrappers; + +import org.quartz.JobKey; +import org.quartz.TriggerKey; +import org.terracotta.quartz.collections.ToolkitDSHolder; +import org.terracotta.toolkit.store.ToolkitStore; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public class TriggerFacade { + private final ToolkitStore triggersByFQN; + private final Set allTriggersGroupNames; + private final Set pausedTriggerGroupNames; + private final ToolkitStore firedTriggers; + + public TriggerFacade(ToolkitDSHolder toolkitDSHolder) { + this.triggersByFQN = toolkitDSHolder.getOrCreateTriggersMap(); + this.allTriggersGroupNames = toolkitDSHolder.getOrCreateAllTriggersGroupsSet(); + this.pausedTriggerGroupNames = toolkitDSHolder.getOrCreatePausedTriggerGroupsSet(); + this.firedTriggers = toolkitDSHolder.getOrCreateFiredTriggersMap(); + } + + public TriggerWrapper get(TriggerKey key) { + return triggersByFQN.get(key); + } + + public boolean containsKey(TriggerKey key) { + return triggersByFQN.containsKey(key); + } + + public void put(TriggerKey key, TriggerWrapper value) { + triggersByFQN.putNoReturn(key, value); + } + + public TriggerWrapper remove(TriggerKey key) { + return triggersByFQN.remove(key); + } + + public FiredTrigger getFiredTrigger(String key) { + return firedTriggers.get(key); + } + + public boolean containsFiredTrigger(String key) { + return firedTriggers.containsKey(key); + } + + public void putFiredTrigger(String key, FiredTrigger value) { + firedTriggers.putNoReturn(key, value); + } + + public FiredTrigger removeFiredTrigger(String key) { + return firedTriggers.remove(key); + } + + public boolean addGroup(String name) { + return this.allTriggersGroupNames.add(name); + } + + public boolean hasGroup(String name) { + return this.allTriggersGroupNames.contains(name); + } + + public boolean removeGroup(String name) { + return this.allTriggersGroupNames.remove(name); + } + + public boolean addPausedGroup(String name) { + return this.pausedTriggerGroupNames.add(name); + } + + public boolean pausedGroupsContain(String name) { + return this.pausedTriggerGroupNames.contains(name); + } + + public boolean removePausedGroup(String name) { + return this.pausedTriggerGroupNames.remove(name); + } + + public Set allTriggersGroupNames() { + return this.allTriggersGroupNames; + } + + public Set allPausedTriggersGroupNames() { + return this.pausedTriggerGroupNames; + } + + public Set allTriggerKeys() { + return this.triggersByFQN.keySet(); + } + + public Collection allFiredTriggers() { + return firedTriggers.values(); + } + + public int numberOfTriggers() { + return triggersByFQN.size(); + } + + public List getTriggerWrappersForJob(JobKey key) { + List trigList = new ArrayList(); + + for (TriggerKey triggerKey : triggersByFQN.keySet()) { + TriggerWrapper tw = triggersByFQN.get(triggerKey); + if (tw.getJobKey().equals(key)) { + trigList.add(tw); + } + } + + return trigList; + } + + public List getTriggerWrappersForCalendar(String calName) { + List trigList = new ArrayList(); + + for (TriggerKey triggerKey : triggersByFQN.keySet()) { + TriggerWrapper tw = triggersByFQN.get(triggerKey); + String tcalName = tw.getCalendarName(); + if (tcalName != null && tcalName.equals(calName)) { + trigList.add(tw); + } + } + + return trigList; + } + + public void removeAllPausedGroups(Collection groups) { + this.pausedTriggerGroupNames.removeAll(groups); + } +} Index: 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/TriggerWrapper.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/TriggerWrapper.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/TriggerWrapper.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,140 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz.wrappers; + +import org.quartz.Calendar; +import org.quartz.JobKey; +import org.quartz.TriggerKey; +import org.quartz.spi.OperableTrigger; + +import java.io.Serializable; +import java.util.Date; + +public class TriggerWrapper implements Serializable { + public enum TriggerState { + WAITING, ACQUIRED, COMPLETE, PAUSED, BLOCKED, PAUSED_BLOCKED, ERROR; + } + + private final boolean jobDisallowsConcurrence; + + private volatile String lastTerracotaClientId = null; + private volatile TriggerState state = TriggerState.WAITING; + + protected final OperableTrigger trigger; + + protected TriggerWrapper(OperableTrigger trigger, boolean jobDisallowsConcurrence) { + this.trigger = trigger; + this.jobDisallowsConcurrence = jobDisallowsConcurrence; + + // TriggerWrapper instances get shared in many collections and the serialized form + // might be referenced before this wrapper makes it into the "timeTriggers" set + // DEV-4807 + // serialize(serializer); + } + + public boolean jobDisallowsConcurrence() { + return jobDisallowsConcurrence; + } + + public String getLastTerracotaClientId() { + return lastTerracotaClientId; + } + + @Override + public int hashCode() { + return getKey().hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "( " + state + ", lastTC=" + lastTerracotaClientId + ", " + trigger + ")"; + } + + public TriggerKey getKey() { + return trigger.getKey(); + } + + public JobKey getJobKey() { + return trigger.getJobKey(); + } + + public void setState(TriggerState state, String terracottaId, TriggerFacade triggerFacade) { + if (terracottaId == null) { throw new NullPointerException(); } + + this.state = state; + this.lastTerracotaClientId = terracottaId; + + rePut(triggerFacade); + } + + public TriggerState getState() { + return state; + } + + public Date getNextFireTime() { + return this.trigger.getNextFireTime(); + } + + public int getPriority() { + return this.trigger.getPriority(); + } + + public boolean mayFireAgain() { + return this.trigger.mayFireAgain(); + } + + public OperableTrigger getTriggerClone() { + return (OperableTrigger) this.trigger.clone(); + } + + public void updateWithNewCalendar(Calendar cal, long misfireThreshold, TriggerFacade triggerFacade) { + this.trigger.updateWithNewCalendar(cal, misfireThreshold); + rePut(triggerFacade); + } + + public String getCalendarName() { + return this.trigger.getCalendarName(); + } + + public int getMisfireInstruction() { + return this.trigger.getMisfireInstruction(); + } + + public void updateAfterMisfire(Calendar cal, TriggerFacade triggerFacade) { + this.trigger.updateAfterMisfire(cal); + rePut(triggerFacade); + } + + public void setFireInstanceId(String firedInstanceId, TriggerFacade triggerFacade) { + this.trigger.setFireInstanceId(firedInstanceId); + rePut(triggerFacade); + } + + public void triggered(Calendar cal, TriggerFacade triggerFacade) { + this.trigger.triggered(cal); + rePut(triggerFacade); + } + + private void rePut(TriggerFacade triggerFacade) { + triggerFacade.put(trigger.getKey(), this); + } + + public boolean isInstanceof(Class clazz) { + return clazz.isInstance(trigger); + } +} \ No newline at end of file Index: 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/WrapperFactory.java =================================================================== diff -u --- 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/WrapperFactory.java (revision 0) +++ 3rdParty_sources/quartz/org/terracotta/quartz/wrappers/WrapperFactory.java (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,27 @@ +/* + * All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved. + * + * 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 org.terracotta.quartz.wrappers; + +import org.quartz.JobDetail; +import org.quartz.spi.OperableTrigger; + +public interface WrapperFactory { + JobWrapper createJobWrapper(JobDetail jobDetail); + + TriggerWrapper createTriggerWrapper(OperableTrigger trigger, boolean jobDisallowsConcurrence); +} Index: 3rdParty_sources/versions.txt =================================================================== diff -u -r0fdf00ad8ffebc0cc6d79de96a216c08ce0d4cdf -rc208628989d52041b3765784f4c8cbfd6c80d47b --- 3rdParty_sources/versions.txt (.../versions.txt) (revision 0fdf00ad8ffebc0cc6d79de96a216c08ce0d4cdf) +++ 3rdParty_sources/versions.txt (.../versions.txt) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -38,7 +38,7 @@ openws 1.5.0 -Quartz 1.5.2 +Quartz 2.2.1 Servlet API 3.1 Index: lams_admin/src/java/org/lamsfoundation/lams/admin/web/ScheduledJobListAction.java =================================================================== diff -u -r08950e1090443c3423a3d1c587416a2fccd8bbdf -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_admin/src/java/org/lamsfoundation/lams/admin/web/ScheduledJobListAction.java (.../ScheduledJobListAction.java) (revision 08950e1090443c3423a3d1c587416a2fccd8bbdf) +++ lams_admin/src/java/org/lamsfoundation/lams/admin/web/ScheduledJobListAction.java (.../ScheduledJobListAction.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -25,6 +25,8 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.List; +import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -36,65 +38,74 @@ import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.quartz.JobDetail; +import org.quartz.JobKey; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; +import org.quartz.impl.matchers.GroupMatcher; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; + /** * * @author Steve.Ni * @version $Revision$ * - * @struts:action path="/joblist" - * validate="false" + * @struts:action path="/joblist" validate="false" * * @struts:action-forward name="list" path=".joblist" */ -public class ScheduledJobListAction extends Action{ +public class ScheduledJobListAction extends Action { - private static final Logger log = Logger.getLogger(ScheduledJobListAction.class); - /** - * Get all waitting queue jobs scheduled in Quartz table and display job name, job start time and - * description. The description will be in format "Lesson Name":"the lesson creator", or - * "The gate name":"The relatived lesson name". + private static final Logger log = Logger.getLogger(ScheduledJobListAction.class); + + /** + * Get all waitting queue jobs scheduled in Quartz table and display job name, job start time and description. The + * description will be in format "Lesson Name":"the lesson creator", or "The gate name":"The relatived lesson name". * - * @param mapping The ActionMapping used to select this instance - * @param actionForm The optional ActionForm bean for this request (if any) - * @param request The HTTP request we are processing - * @param response The HTTP response we are creating + * @param mapping + * The ActionMapping used to select this instance + * @param actionForm + * The optional ActionForm bean for this request (if any) + * @param request + * The HTTP request we are processing + * @param response + * The HTTP response we are creating * - * @exception IOException if an input/output error occurs - * @exception ServletException if a servlet exception occurs + * @exception IOException + * if an input/output error occurs + * @exception ServletException + * if a servlet exception occurs * */ - public ActionForward execute(ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) throws Exception{ - - WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(this.getServlet().getServletContext()); - Scheduler scheduler = (Scheduler) ctx.getBean("scheduler"); - ArrayList jobList = new ArrayList(); - try { - String[] jobNames = scheduler.getJobNames(Scheduler.DEFAULT_GROUP); - for (String name : jobNames) { - ScheduledJobDTO jobDto = new ScheduledJobDTO(); - JobDetail detail = scheduler.getJobDetail(name,Scheduler.DEFAULT_GROUP); - jobDto.setName(name); - jobDto.setDescription(detail.getDescription()); - Trigger[] triggers = scheduler.getTriggersOfJob(name,Scheduler.DEFAULT_GROUP); - for (Trigger trigger : triggers) { - jobDto.setStartDate(trigger.getStartTime()); - jobList.add(jobDto); - } - } - } catch (SchedulerException e) { - log.equals("Failed get job names:" + e.getMessage()); + @SuppressWarnings("unchecked") + @Override + public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws Exception { + + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(this.getServlet() + .getServletContext()); + Scheduler scheduler = (Scheduler) ctx.getBean("scheduler"); + ArrayList jobList = new ArrayList(); + try { + Set jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(Scheduler.DEFAULT_GROUP)); + for (JobKey jobKey : jobKeys) { + ScheduledJobDTO jobDto = new ScheduledJobDTO(); + JobDetail detail = scheduler.getJobDetail(jobKey); + jobDto.setName(jobKey.getName()); + jobDto.setDescription(detail.getDescription()); + List triggers = (List) scheduler.getTriggersOfJob(jobKey); + for (Trigger trigger : triggers) { + jobDto.setStartDate(trigger.getStartTime()); + jobList.add(jobDto); } - - request.setAttribute("jobList",jobList); - return mapping.findForward("list"); + } + } catch (SchedulerException e) { + ScheduledJobListAction.log.equals("Failed get job names:" + e.getMessage()); + } + + request.setAttribute("jobList", jobList); + return mapping.findForward("list"); } - + } Index: lams_build/3rdParty.userlibraries =================================================================== diff -u -rc6b29814cfe36cd678a7cdd1d7ed0aab7d0fbec8 -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision c6b29814cfe36cd678a7cdd1d7ed0aab7d0fbec8) +++ lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -31,7 +31,6 @@ - @@ -43,5 +42,6 @@ + Index: lams_build/build.xml =================================================================== diff -u -rfcbf6ec1b1053eb093afa423877716fa707a6081 -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_build/build.xml (.../build.xml) (revision fcbf6ec1b1053eb093afa423877716fa707a6081) +++ lams_build/build.xml (.../build.xml) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -619,7 +619,7 @@ - + - + - + \ No newline at end of file Index: lams_build/liblist.txt =================================================================== diff -u -rc6b29814cfe36cd678a7cdd1d7ed0aab7d0fbec8 -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_build/liblist.txt (.../liblist.txt) (revision c6b29814cfe36cd678a7cdd1d7ed0aab7d0fbec8) +++ lams_build/liblist.txt (.../liblist.txt) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -48,7 +48,7 @@ odmg odmg-3.0.jar 3.0 -quartz quartz.jar 1.5.3 Apache License 2.0 Terracotta For running scheduled jobs +quartz quartz-2.2.1.jar 2.2.1 Apache License 2.0 Terracotta For running scheduled jobs reload reload-diva.jar 2.0.1 MIT License Reload Suite of tools for viewing, editing and creating IMS and SCORM Content Packages reload-jdom.jar Index: lams_common/src/java/org/lamsfoundation/lams/commonContext.xml =================================================================== diff -u -r4d65537fb74ec9a261ccb55520fe28c1253e9b13 -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision 4d65537fb74ec9a261ccb55520fe28c1253e9b13) +++ lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -227,6 +227,7 @@ + org.springframework.scheduling.quartz.LocalDataSourceJobStore @@ -278,14 +279,13 @@ - + + class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> - Index: lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch02040038.sql =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch02040038.sql (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch02040038.sql (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -0,0 +1,157 @@ +SET AUTOCOMMIT = 0; +SET FOREIGN_KEY_CHECKS = 0; + +-- LDEV-3337: Update Quartz library to versiON 2.2.1 + +-- drop tables that are no longer used + +DROP TABLE lams_qtz_job_listeners; +DROP TABLE lams_qtz_trigger_listeners; + +-- drop columns that are no longer used + +ALTER TABLE lams_qtz_job_details DROP COLUMN is_volatile; +ALTER TABLE lams_qtz_triggers DROP COLUMN is_volatile; +ALTER TABLE lams_qtz_fired_triggers DROP COLUMN is_volatile; +ALTER TABLE lams_qtz_scheduler_state DROP COLUMN recoverer; + +-- add and modify column definitions to match new schema + +ALTER TABLE lams_qtz_job_details MODIFY COLUMN job_name VARCHAR(200); +ALTER TABLE lams_qtz_job_details MODIFY COLUMN job_group VARCHAR(200); +ALTER TABLE lams_qtz_job_details MODIFY COLUMN description VARCHAR(250); +ALTER TABLE lams_qtz_job_details MODIFY COLUMN job_class_name VARCHAR(250); +ALTER TABLE lams_qtz_triggers MODIFY COLUMN trigger_name VARCHAR(200); +ALTER TABLE lams_qtz_triggers MODIFY COLUMN trigger_group VARCHAR(200); +ALTER TABLE lams_qtz_triggers MODIFY COLUMN job_name VARCHAR(200); +ALTER TABLE lams_qtz_triggers MODIFY COLUMN job_group VARCHAR(200); +ALTER TABLE lams_qtz_triggers MODIFY COLUMN description VARCHAR(250); +ALTER TABLE lams_qtz_triggers MODIFY COLUMN calendar_name VARCHAR(200); +ALTER TABLE lams_qtz_triggers ADD COLUMN priority INT AFTER prev_fire_time; +ALTER TABLE lams_qtz_simple_triggers MODIFY COLUMN trigger_name VARCHAR(200); +ALTER TABLE lams_qtz_simple_triggers MODIFY COLUMN trigger_group VARCHAR(200); +ALTER TABLE lams_qtz_simple_triggers MODIFY COLUMN times_triggered BIGINT(10); +ALTER TABLE lams_qtz_cron_triggers MODIFY COLUMN trigger_name VARCHAR(200); +ALTER TABLE lams_qtz_cron_triggers MODIFY COLUMN trigger_group VARCHAR(200); +ALTER TABLE lams_qtz_cron_triggers MODIFY COLUMN cron_expression VARCHAR(120); +ALTER TABLE lams_qtz_blob_triggers MODIFY COLUMN trigger_name VARCHAR(200); +ALTER TABLE lams_qtz_blob_triggers MODIFY COLUMN trigger_group VARCHAR(200); +ALTER TABLE lams_qtz_calendars MODIFY COLUMN calendar_name VARCHAR(200); +ALTER TABLE lams_qtz_paused_trigger_grps MODIFY COLUMN trigger_group VARCHAR(200); +ALTER TABLE lams_qtz_fired_triggers MODIFY COLUMN trigger_name VARCHAR(200); +ALTER TABLE lams_qtz_fired_triggers MODIFY COLUMN trigger_group VARCHAR(200); +ALTER TABLE lams_qtz_fired_triggers MODIFY COLUMN instance_name VARCHAR(200); +ALTER TABLE lams_qtz_fired_triggers MODIFY COLUMN job_name VARCHAR(200); +ALTER TABLE lams_qtz_fired_triggers MODIFY COLUMN job_group VARCHAR(200); +ALTER TABLE lams_qtz_fired_triggers ADD COLUMN sched_time BIGINT(13) NOT NULL AFTER fired_time; +ALTER TABLE lams_qtz_fired_triggers ADD COLUMN priority INTEGER NOT NULL AFTER sched_time; +ALTER TABLE lams_qtz_scheduler_state MODIFY COLUMN instance_name VARCHAR(200); + +-- add new columns that replace the 'is_stateful' column + +ALTER TABLE lams_qtz_job_details ADD COLUMN is_nonconcurrent bool AFTER is_durable; +ALTER TABLE lams_qtz_job_details ADD COLUMN is_update_data bool AFTER is_nonconcurrent; +UPDATE lams_qtz_job_details SET is_nonconcurrent = is_stateful; +UPDATE lams_qtz_job_details SET is_update_data = is_stateful; +ALTER TABLE lams_qtz_job_details DROP COLUMN is_stateful; +ALTER TABLE lams_qtz_fired_triggers ADD COLUMN is_nonconcurrent bool; +UPDATE lams_qtz_fired_triggers SET is_nonconcurrent = is_stateful; +ALTER TABLE lams_qtz_fired_triggers DROP COLUMN is_stateful; + +-- add new 'sched_name' COLUMN to all tables + +ALTER TABLE lams_qtz_blob_triggers ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; +ALTER TABLE lams_qtz_calendars ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; +ALTER TABLE lams_qtz_cron_triggers ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; +ALTER TABLE lams_qtz_fired_triggers ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; +ALTER TABLE lams_qtz_job_details ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; +ALTER TABLE lams_qtz_locks ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; +ALTER TABLE lams_qtz_paused_trigger_grps ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; +ALTER TABLE lams_qtz_scheduler_state ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; +ALTER TABLE lams_qtz_simple_triggers ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; +ALTER TABLE lams_qtz_triggers ADD COLUMN sched_name VARCHAR(120) NOT NULL DEFAULT 'LamsQuartzScheduler' FIRST; + +-- drop all primary and foreign key constraints, so that we can define new ones + +ALTER TABLE lams_qtz_triggers DROP FOREIGN KEY lams_qtz_triggers_ibfk_1; +ALTER TABLE lams_qtz_triggers DROP INDEX job_name; +ALTER TABLE lams_qtz_blob_triggers DROP FOREIGN KEY lams_qtz_blob_triggers_ibfk_1; +ALTER TABLE lams_qtz_blob_triggers DROP PRIMARY KEY; +ALTER TABLE lams_qtz_simple_triggers DROP FOREIGN KEY lams_qtz_simple_triggers_ibfk_1; +ALTER TABLE lams_qtz_simple_triggers DROP PRIMARY KEY; +ALTER TABLE lams_qtz_cron_triggers DROP FOREIGN KEY lams_qtz_cron_triggers_ibfk_1; +ALTER TABLE lams_qtz_cron_triggers DROP PRIMARY KEY; +ALTER TABLE lams_qtz_job_details DROP PRIMARY KEY; +ALTER TABLE lams_qtz_job_details ADD PRIMARY KEY (sched_name, job_name, job_group); +ALTER TABLE lams_qtz_triggers DROP PRIMARY KEY; + +-- add all primary and foreign key constraints, based on new columns + +ALTER TABLE lams_qtz_triggers ADD PRIMARY KEY (sched_name, trigger_name, trigger_group); +ALTER TABLE lams_qtz_triggers ADD FOREIGN KEY (sched_name, job_name, job_group) REFERENCES lams_qtz_job_details(sched_name, job_name, job_group); +ALTER TABLE lams_qtz_blob_triggers ADD PRIMARY KEY (sched_name, trigger_name, trigger_group); +ALTER TABLE lams_qtz_blob_triggers ADD FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES lams_qtz_triggers(sched_name, trigger_name, trigger_group); +ALTER TABLE lams_qtz_cron_triggers ADD PRIMARY KEY (sched_name, trigger_name, trigger_group); +ALTER TABLE lams_qtz_cron_triggers ADD FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES lams_qtz_triggers(sched_name, trigger_name, trigger_group); +ALTER TABLE lams_qtz_simple_triggers ADD PRIMARY KEY (sched_name, trigger_name, trigger_group); +ALTER TABLE lams_qtz_simple_triggers ADD FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES lams_qtz_triggers(sched_name, trigger_name, trigger_group); +ALTER TABLE lams_qtz_fired_triggers DROP PRIMARY KEY; +ALTER TABLE lams_qtz_fired_triggers ADD PRIMARY KEY (sched_name, entry_id); +ALTER TABLE lams_qtz_calendars DROP PRIMARY KEY; +ALTER TABLE lams_qtz_calendars ADD PRIMARY KEY (sched_name, calendar_name); +ALTER TABLE lams_qtz_locks DROP PRIMARY KEY; +ALTER TABLE lams_qtz_locks ADD PRIMARY KEY (sched_name, lock_name); +ALTER TABLE lams_qtz_paused_trigger_grps DROP PRIMARY KEY; +ALTER TABLE lams_qtz_paused_trigger_grps ADD PRIMARY KEY (sched_name, trigger_group); +ALTER TABLE lams_qtz_scheduler_state DROP PRIMARY KEY; +ALTER TABLE lams_qtz_scheduler_state ADD PRIMARY KEY (sched_name, instance_name); + +-- add new simprop_triggers table + +CREATE TABLE lams_qtz_simprop_triggers + ( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + STR_PROP_1 VARCHAR(512) NULL, + STR_PROP_2 VARCHAR(512) NULL, + STR_PROP_3 VARCHAR(512) NULL, + INT_PROP_1 INT NULL, + INT_PROP_2 INT NULL, + LONG_PROP_1 BIGINT NULL, + LONG_PROP_2 BIGINT NULL, + DEC_PROP_1 NUMERIC(13,4) NULL, + DEC_PROP_2 NUMERIC(13,4) NULL, + BOOL_PROP_1 BOOL NULL, + BOOL_PROP_2 BOOL NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES lams_qtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +) ENGINE=InnoDB; + +-- create indexes for faster queries + +CREATE INDEX idx_lams_qtz_j_req_recovery ON lams_qtz_job_details(SCHED_NAME,REQUESTS_RECOVERY); +CREATE INDEX idx_lams_qtz_j_grp ON lams_qtz_job_details(SCHED_NAME,JOB_GROUP); +CREATE INDEX idx_lams_qtz_t_j ON lams_qtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); +CREATE INDEX idx_lams_qtz_t_jg ON lams_qtz_triggers(SCHED_NAME,JOB_GROUP); +CREATE INDEX idx_lams_qtz_t_c ON lams_qtz_triggers(SCHED_NAME,CALENDAR_NAME); +CREATE INDEX idx_lams_qtz_t_g ON lams_qtz_triggers(SCHED_NAME,TRIGGER_GROUP); +CREATE INDEX idx_lams_qtz_t_state ON lams_qtz_triggers(SCHED_NAME,TRIGGER_STATE); +CREATE INDEX idx_lams_qtz_t_n_state ON lams_qtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); +CREATE INDEX idx_lams_qtz_t_n_g_state ON lams_qtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); +CREATE INDEX idx_lams_qtz_t_next_fire_time ON lams_qtz_triggers(SCHED_NAME,NEXT_FIRE_TIME); +CREATE INDEX idx_lams_qtz_t_nft_st ON lams_qtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); +CREATE INDEX idx_lams_qtz_t_nft_misfire ON lams_qtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); +CREATE INDEX idx_lams_qtz_t_nft_st_misfire ON lams_qtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); +CREATE INDEX idx_lams_qtz_t_nft_st_misfire_grp ON lams_qtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); +CREATE INDEX idx_lams_qtz_ft_trig_inst_name ON lams_qtz_fired_triggers(SCHED_NAME,INSTANCE_NAME); +CREATE INDEX idx_lams_qtz_ft_inst_job_req_rcvry ON lams_qtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); +CREATE INDEX idx_lams_qtz_ft_j_g ON lams_qtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); +CREATE INDEX idx_lams_qtz_ft_jg ON lams_qtz_fired_triggers(SCHED_NAME,JOB_GROUP); +CREATE INDEX idx_lams_qtz_ft_t_g ON lams_qtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); +CREATE INDEX idx_lams_qtz_ft_tg ON lams_qtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP); + +COMMIT; +SET AUTOCOMMIT = 1; +SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/util/Configuration.java =================================================================== diff -u -rb6ed1309cf89ecede6717145c629d7cb3b842582 -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_common/src/java/org/lamsfoundation/lams/util/Configuration.java (.../Configuration.java) (revision b6ed1309cf89ecede6717145c629d7cb3b842582) +++ lams_common/src/java/org/lamsfoundation/lams/util/Configuration.java (.../Configuration.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -39,10 +39,13 @@ import org.lamsfoundation.lams.config.dao.IConfigurationDAO; import org.lamsfoundation.lams.config.dao.IRegistrationDAO; import org.lamsfoundation.lams.usermanagement.WorkspaceFolder; +import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; -import org.quartz.SimpleTrigger; +import org.quartz.SimpleScheduleBuilder; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; import org.springframework.beans.factory.InitializingBean; /** @@ -188,18 +191,14 @@ } String refreshCacheIntervalString = Configuration.get(ConfigurationKeys.CONFIGURATION_CACHE_REFRESH_INTERVAL); - Long refreshCacheInterval = StringUtils.isBlank(refreshCacheIntervalString) ? null : Long + Integer refreshCacheInterval = StringUtils.isBlank(refreshCacheIntervalString) ? null : Integer .valueOf(refreshCacheIntervalString); if ((refreshCacheInterval != null) && (refreshCacheInterval > 0)) { - SimpleTrigger trigger = new SimpleTrigger("configurationCacheRefreshTrigger", null); - trigger.setVolatility(true); - trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); - trigger.setRepeatInterval(Long.valueOf(refreshCacheInterval) * 60 * 1000); + JobDetail jobDetail = JobBuilder.newJob(ConfigurationRefreshCacheJob.class) + .withIdentity("configurationCacheRefresh").build(); + Trigger trigger = TriggerBuilder.newTrigger().withIdentity("configurationCacheRefreshTrigger") + .withSchedule(SimpleScheduleBuilder.repeatMinutelyForever(refreshCacheInterval)).build(); - JobDetail jobDetail = new JobDetail("configurationCacheRefresh", null, ConfigurationRefreshCacheJob.class); - // do not store in DB as the job will be recreated after LAMS restart anyway - jobDetail.setVolatility(true); - try { Configuration.scheduler.scheduleJob(jobDetail, trigger); } catch (SchedulerException e) { @@ -290,7 +289,7 @@ Configuration.setSystemProperty(ConfigurationKeys.TRUSTSTORE_PASSWORD, Configuration.get(ConfigurationKeys.TRUSTSTORE_PASSWORD)); updatePublicFolderName(); - configurationDAO.insertOrUpdateAll(Configuration.items.values()); + Configuration.configurationDAO.insertOrUpdateAll(Configuration.items.values()); } /** @@ -322,7 +321,7 @@ private void updatePublicFolderName() { // LDEV-2430 update public folder name according to default server locale WorkspaceFolder publicFolder = null; - List list = configurationDAO.findByProperty(WorkspaceFolder.class, + List list = Configuration.configurationDAO.findByProperty(WorkspaceFolder.class, "workspaceFolderType", WorkspaceFolder.PUBLIC_SEQUENCES); if ((list != null) && (list.size() > 0)) { @@ -331,7 +330,7 @@ Locale locale = new Locale(langCountry[0], langCountry[1]); publicFolder.setName(Configuration.messageService.getMessageSource().getMessage("public.folder.name", null, locale)); - configurationDAO.update(publicFolder); + Configuration.configurationDAO.update(publicFolder); } } } \ No newline at end of file Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/MonitoringConstants.java =================================================================== diff -u -r6432c99a2551127c11e8d8fd10df6abdbfe1fe34 -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/MonitoringConstants.java (.../MonitoringConstants.java) (revision 6432c99a2551127c11e8d8fd10df6abdbfe1fe34) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/MonitoringConstants.java (.../MonitoringConstants.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -36,9 +36,6 @@ public static final String KEY_USER_ID = "userID"; public static final String KEY_STAFF = "staff"; public static final String KEY_LEARNER = "learners"; - public static final String JOB_START_LESSON = "startScheduleLessonJob"; - public static final String JOB_FINISH_LESSON = "finishScheduleLessonJob"; - public static final String JOB_EMAIL_MESSAGE = "emailScheduleMessageJob"; public static final String PARAM_LESSON_START_DATE = "lessonStartDate"; public static final String PARAM_SCHEDULED_NUMBER_DAYS_TO_LESSON_FINISH = "scheduledNumberDaysToLessonFinish"; public static final String PARAM_LEARNER_ID = "learnerID"; Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/monitoringApplicationContext.xml =================================================================== diff -u -r7bba5023611f36fc23efef242a11bb7fa87e9112 -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/monitoringApplicationContext.xml (.../monitoringApplicationContext.xml) (revision 7bba5023611f36fc23efef242a11bb7fa87e9112) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/monitoringApplicationContext.xml (.../monitoringApplicationContext.xml) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -74,32 +74,4 @@ - - - - org.lamsfoundation.lams.monitoring.quartz.job.OpenScheduleGateJob - - - - - - org.lamsfoundation.lams.monitoring.quartz.job.CloseScheduleGateJob - - - - - org.lamsfoundation.lams.monitoring.quartz.job.StartScheduleLessonJob - - - - - org.lamsfoundation.lams.monitoring.quartz.job.FinishScheduleLessonJob - - - - - org.lamsfoundation.lams.monitoring.quartz.job.EmailScheduleMessageJob - - - \ No newline at end of file Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== diff -u -r947070b84c39d750f794eeaddf11dfb5818c58a0 -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 947070b84c39d750f794eeaddf11dfb5818c58a0) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -83,6 +83,10 @@ import org.lamsfoundation.lams.logevent.service.ILogEventService; import org.lamsfoundation.lams.monitoring.MonitoringConstants; import org.lamsfoundation.lams.monitoring.dto.ContributeActivityDTO; +import org.lamsfoundation.lams.monitoring.quartz.job.CloseScheduleGateJob; +import org.lamsfoundation.lams.monitoring.quartz.job.FinishScheduleLessonJob; +import org.lamsfoundation.lams.monitoring.quartz.job.OpenScheduleGateJob; +import org.lamsfoundation.lams.monitoring.quartz.job.StartScheduleLessonJob; import org.lamsfoundation.lams.security.ISecurityService; import org.lamsfoundation.lams.tool.ToolSession; import org.lamsfoundation.lams.tool.exception.LamsToolServiceException; @@ -105,11 +109,13 @@ import org.lamsfoundation.lams.util.wddx.WDDXTAGS; import org.lamsfoundation.lams.web.session.SessionManager; import org.lamsfoundation.lams.web.util.AttributeNames; +import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; -import org.quartz.SimpleTrigger; import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -585,18 +591,20 @@ TimeZone userTimeZone = TimeZone.getTimeZone(user.getTimeZone()); Date tzStartLessonDate = DateUtil.convertFromTimeZoneToDefault(userTimeZone, startDate); - JobDetail startLessonJob = getStartScheduleLessonJob(); // setup the message for scheduling job - startLessonJob.setName("startLessonOnSchedule:" + lessonId); + JobDetail startLessonJob = JobBuilder + .newJob(StartScheduleLessonJob.class) + .withIdentity("startLessonOnSchedule:" + lessonId) + .withDescription( + requestedLesson.getLessonName() + ":" + + (requestedLesson.getUser() == null ? "" : requestedLesson.getUser().getFullName())) + .usingJobData(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)) + .usingJobData(MonitoringConstants.KEY_USER_ID, new Integer(userId)).build(); - startLessonJob.setDescription(requestedLesson.getLessonName() + ":" - + (requestedLesson.getUser() == null ? "" : requestedLesson.getUser().getFullName())); - startLessonJob.getJobDataMap().put(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)); - startLessonJob.getJobDataMap().put(MonitoringConstants.KEY_USER_ID, new Integer(userId)); - // create customized triggers - Trigger startLessonTrigger = new SimpleTrigger("startLessonOnScheduleTrigger:" + lessonId, - Scheduler.DEFAULT_GROUP, tzStartLessonDate); + Trigger startLessonTrigger = TriggerBuilder.newTrigger() + .withIdentity("startLessonOnScheduleTrigger:" + lessonId).startAt(tzStartLessonDate).build(); + // start the scheduling job try { requestedLesson.setScheduleStartDate(tzStartLessonDate); @@ -619,19 +627,17 @@ // we get the lesson want to finish Lesson requestedLesson = lessonDAO.getLesson(new Long(lessonId)); String triggerName = "finishLessonOnScheduleTrigger:" + lessonId; + Trigger finishLessonTrigger = null; + JobDetail finishLessonJob = null; boolean alreadyScheduled = false; try { - // if trigger exists, the job was already scheduled and we need to (re)move the trigger - alreadyScheduled = scheduler.getTrigger(triggerName, Scheduler.DEFAULT_GROUP) != null; + finishLessonTrigger = scheduler.getTrigger(TriggerKey.triggerKey(triggerName)); + alreadyScheduled = finishLessonTrigger != null; } catch (SchedulerException e) { - MonitoringService.log.error(e); + MonitoringService.log.error("Error while fetching Quartz trigger \"" + triggerName + "\"", e); } - Trigger finishLessonTrigger = null; - String finishLessonJobName = "finishLessonOnSchedule:" + lessonId; - JobDetail finishLessonJob = null; Date endDate = null; - if (scheduledNumberDaysToLessonFinish > 0) { // calculate finish date Date startDate = (requestedLesson.getStartDateTime() != null) ? requestedLesson.getStartDateTime() @@ -649,19 +655,23 @@ throw new IllegalArgumentException("Lesson scheduled finish date is already in the past"); } - if (!alreadyScheduled) { - finishLessonJob = getFinishScheduleLessonJob(); + if (alreadyScheduled) { + finishLessonTrigger = finishLessonTrigger.getTriggerBuilder().startAt(endDate).build(); + } else { // setup the message for scheduling job - finishLessonJob.setName(finishLessonJobName); - finishLessonJob.setDescription(requestedLesson.getLessonName() + ":" - + (requestedLesson.getUser() == null ? "" : requestedLesson.getUser().getFullName())); - finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)); - finishLessonJob.getJobDataMap().put(MonitoringConstants.KEY_USER_ID, new Integer(userId)); - } + finishLessonJob = JobBuilder + .newJob(FinishScheduleLessonJob.class) + .withIdentity("finishLessonOnSchedule:" + lessonId) + .withDescription( + requestedLesson.getLessonName() + + ":" + + (requestedLesson.getUser() == null ? "" : requestedLesson.getUser() + .getFullName())) + .usingJobData(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)) + .usingJobData(MonitoringConstants.KEY_USER_ID, new Integer(userId)).build(); - // create customized triggers - finishLessonTrigger = new SimpleTrigger(triggerName, Scheduler.DEFAULT_GROUP, endDate); - finishLessonTrigger.setJobName(finishLessonJobName); + finishLessonTrigger = TriggerBuilder.newTrigger().withIdentity(triggerName).startAt(endDate).build(); + } } // start the scheduling job @@ -670,13 +680,13 @@ lessonDAO.updateLesson(requestedLesson); if (alreadyScheduled) { if (scheduledNumberDaysToLessonFinish > 0) { - scheduler.rescheduleJob(triggerName, Scheduler.DEFAULT_GROUP, finishLessonTrigger); + scheduler.rescheduleJob(finishLessonTrigger.getKey(), finishLessonTrigger); if (MonitoringService.log.isDebugEnabled()) { MonitoringService.log.debug("Finish lesson [" + lessonId + "] job has been rescheduled to " + endDate); } } else { - scheduler.deleteJob(finishLessonJobName, Scheduler.DEFAULT_GROUP); + scheduler.deleteJob(finishLessonTrigger.getJobKey()); if (MonitoringService.log.isDebugEnabled()) { MonitoringService.log.debug("Finish lesson [" + lessonId + "] job has been removed"); } @@ -784,26 +794,28 @@ @Override public ScheduleGateActivity runGateScheduler(ScheduleGateActivity scheduleGate, Date schedulingStartTime, String lessonName) { - if (MonitoringService.log.isDebugEnabled()) { MonitoringService.log.debug("Running scheduler for gate " + scheduleGate.getActivityId() + "..."); } - JobDetail openScheduleGateJob = getOpenScheduleGateJob(); - JobDetail closeScheduleGateJob = getCloseScheduleGateJob(); + // setup the message for scheduling job - openScheduleGateJob.setName("openGate:" + scheduleGate.getActivityId()); - openScheduleGateJob.setDescription(scheduleGate.getTitle() + ":" + lessonName); - openScheduleGateJob.getJobDataMap().put("gateId", scheduleGate.getActivityId()); - closeScheduleGateJob.setName("closeGate:" + scheduleGate.getActivityId()); - closeScheduleGateJob.getJobDataMap().put("gateId", scheduleGate.getActivityId()); - closeScheduleGateJob.setDescription(scheduleGate.getTitle() + ":" + lessonName); + JobDetail openScheduleGateJob = JobBuilder.newJob(OpenScheduleGateJob.class) + .withIdentity("openGate:" + scheduleGate.getActivityId()) + .withDescription(scheduleGate.getTitle() + ":" + lessonName) + .usingJobData("gateId", scheduleGate.getActivityId()).build(); + JobDetail closeScheduleGateJob = JobBuilder.newJob(CloseScheduleGateJob.class) + .withIdentity("closeGate:" + scheduleGate.getActivityId()) + .withDescription(scheduleGate.getTitle() + ":" + lessonName) + .usingJobData("gateId", scheduleGate.getActivityId()).build(); // create customized triggers - Trigger openGateTrigger = new SimpleTrigger("openGateTrigger:" + scheduleGate.getActivityId(), - Scheduler.DEFAULT_GROUP, scheduleGate.getGateOpenTime(schedulingStartTime)); + Trigger openGateTrigger = TriggerBuilder.newTrigger() + .withIdentity("openGateTrigger:" + scheduleGate.getActivityId()) + .startAt(scheduleGate.getGateOpenTime(schedulingStartTime)).build(); - Trigger closeGateTrigger = new SimpleTrigger("closeGateTrigger:" + scheduleGate.getActivityId(), - Scheduler.DEFAULT_GROUP, scheduleGate.getGateCloseTime(schedulingStartTime)); + Trigger closeGateTrigger = TriggerBuilder.newTrigger() + .withIdentity("closeGateTrigger:" + scheduleGate.getActivityId()) + .startAt(scheduleGate.getGateCloseTime(schedulingStartTime)).build(); // start the scheduling job try { @@ -816,8 +828,7 @@ } } catch (SchedulerException e) { - throw new MonitoringServiceException("Error occurred at " + "[runGateScheduler]- fail to start scheduling", - e); + throw new MonitoringServiceException("Error occurred at [runGateScheduler] - fail to start scheduling", e); } if (MonitoringService.log.isDebugEnabled()) { @@ -985,17 +996,14 @@ // we un-schedule the gate from the scheduler if it's of a scheduled // gate (LDEV-1271) if (gate.isScheduleGate()) { - try { - scheduler.unscheduleJob("openGateTrigger:" + gate.getActivityId(), Scheduler.DEFAULT_GROUP); + scheduler.unscheduleJob(TriggerKey.triggerKey("openGateTrigger:" + gate.getActivityId())); } catch (SchedulerException e) { MonitoringService.log.error( "Error unscheduling trigger for gate activity id:" + gate.getActivityId(), e); throw new MonitoringServiceException("Error unscheduling trigger for gate activity id:" + gate.getActivityId(), e); - } - } activityDAO.update(gate); @@ -1921,40 +1929,6 @@ } // --------------------------------------------------------------------- - // Helper Methods - scheduling - // --------------------------------------------------------------------- - - /** - * Returns the bean that defines the open schedule gate job. - */ - private JobDetail getOpenScheduleGateJob() { - return (JobDetail) applicationContext.getBean("openScheduleGateJob"); - } - - /** - * - * @return the bean that defines start lesson on schedule job. - */ - private JobDetail getStartScheduleLessonJob() { - return (JobDetail) applicationContext.getBean(MonitoringConstants.JOB_START_LESSON); - } - - /** - * - * @return the bean that defines start lesson on schedule job. - */ - private JobDetail getFinishScheduleLessonJob() { - return (JobDetail) applicationContext.getBean(MonitoringConstants.JOB_FINISH_LESSON); - } - - /** - * Returns the bean that defines the close schdule gate job. - */ - private JobDetail getCloseScheduleGateJob() { - return (JobDetail) applicationContext.getBean("closeScheduleGateJob"); - } - - // --------------------------------------------------------------------- // Preview related methods // --------------------------------------------------------------------- Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/EmailNotificationsAction.java =================================================================== diff -u -r4ab4ba6ef63a831648ca63ca4d91edd4c0fba55f -rc208628989d52041b3765784f4c8cbfd6c80d47b --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/EmailNotificationsAction.java (.../EmailNotificationsAction.java) (revision 4ab4ba6ef63a831648ca63ca4d91edd4c0fba55f) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/EmailNotificationsAction.java (.../EmailNotificationsAction.java) (revision c208628989d52041b3765784f4c8cbfd6c80d47b) @@ -52,6 +52,7 @@ import org.lamsfoundation.lams.lesson.util.LessonComparator; import org.lamsfoundation.lams.monitoring.MonitoringConstants; import org.lamsfoundation.lams.monitoring.dto.EmailScheduleMessageJobDTO; +import org.lamsfoundation.lams.monitoring.quartz.job.EmailScheduleMessageJob; import org.lamsfoundation.lams.monitoring.service.IMonitoringService; import org.lamsfoundation.lams.monitoring.service.MonitoringServiceProxy; import org.lamsfoundation.lams.security.ISecurityService; @@ -65,12 +66,15 @@ import org.lamsfoundation.lams.web.action.LamsDispatchAction; import org.lamsfoundation.lams.web.session.SessionManager; import org.lamsfoundation.lams.web.util.AttributeNames; +import org.quartz.JobBuilder; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; -import org.quartz.SimpleTrigger; import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import org.quartz.impl.matchers.GroupMatcher; import org.springframework.util.Assert; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; @@ -99,6 +103,8 @@ // --------------------------------------------------------------------- private static final String TRIGGER_PREFIX_NAME = "emailMessageOnScheduleTrigger:"; + private static final String JOB_PREFIX_NAME = "emailScheduleMessageJob:"; + private static IEventNotificationService eventNotificationService; private static IUserManagementService userManagementService; private static IAuditService auditService; @@ -200,11 +206,13 @@ } } - String[] triggerNames = scheduler.getTriggerNames(Scheduler.DEFAULT_GROUP); - for (String triggerName : triggerNames) { + Set triggerKeys = scheduler + .getTriggerKeys(GroupMatcher.triggerGroupEquals(Scheduler.DEFAULT_GROUP)); + for (TriggerKey triggerKey : triggerKeys) { + String triggerName = triggerKey.getName(); if (triggerName.startsWith(EmailNotificationsAction.TRIGGER_PREFIX_NAME)) { - Trigger trigger = scheduler.getTrigger(triggerName, Scheduler.DEFAULT_GROUP); - JobDetail jobDetail = scheduler.getJobDetail(trigger.getJobName(), Scheduler.DEFAULT_GROUP); + Trigger trigger = scheduler.getTrigger(triggerKey); + JobDetail jobDetail = scheduler.getJobDetail(trigger.getJobKey()); JobDataMap jobDataMap = jobDetail.getJobDataMap(); // filter triggers @@ -258,10 +266,12 @@ for (String userIdStr : userIdStrs) { int userId = Integer.parseInt(userIdStr); boolean isHtmlFormat = false; - isSuccessfullySent &= getEventNotificationService().sendMessage(null, userId, - IEventNotificationService.DELIVERY_METHOD_MAIL, monitoringService.getMessageService() - .getMessage("event.emailnotifications.email.subject", new Object[] {}), emailBody, - isHtmlFormat); + isSuccessfullySent &= getEventNotificationService().sendMessage( + null, + userId, + IEventNotificationService.DELIVERY_METHOD_MAIL, + monitoringService.getMessageService().getMessage("event.emailnotifications.email.subject", + new Object[] {}), emailBody, isHtmlFormat); } JSONObject.put("isSuccessfullySent", isSuccessfullySent); @@ -276,16 +286,17 @@ TimeZone teacherTimeZone = teacher.getTimeZone(); Date scheduleDate = DateUtil.convertFromTimeZoneToDefault(teacherTimeZone, scheduleDateTeacherTimezone); - JobDetail emailScheduleMessageJob = getEmailScheduleMessageJob(); - emailScheduleMessageJob.setName("emailScheduleMessageJob:" + now.getTimeInMillis()); - emailScheduleMessageJob.setDescription("schedule email message to user(s)"); - emailScheduleMessageJob.getJobDataMap().put("emailBody", emailBody); - + // build job detail based on the bean class + JobDetail emailScheduleMessageJob = JobBuilder.newJob(EmailScheduleMessageJob.class) + .withIdentity(EmailNotificationsAction.JOB_PREFIX_NAME + now.getTimeInMillis()) + .withDescription("schedule email message to user(s)").usingJobData("emailBody", emailBody) + .build(); copySearchParametersFromRequestToMap(request, emailScheduleMessageJob.getJobDataMap()); // create customized triggers - Trigger startLessonTrigger = new SimpleTrigger(EmailNotificationsAction.TRIGGER_PREFIX_NAME - + now.getTimeInMillis(), Scheduler.DEFAULT_GROUP, scheduleDate); + Trigger startLessonTrigger = TriggerBuilder.newTrigger() + .withIdentity(EmailNotificationsAction.TRIGGER_PREFIX_NAME + now.getTimeInMillis()) + .startAt(scheduleDate).build(); // start the scheduling job Scheduler scheduler = getScheduler(); scheduler.scheduleJob(emailScheduleMessageJob, startLessonTrigger); @@ -326,7 +337,7 @@ return null; } } - + IMonitoringService monitoringService = MonitoringServiceProxy.getMonitoringService(getServlet() .getServletContext()); ICoreLearnerService learnerService = MonitoringServiceProxy.getLearnerService(getServlet().getServletContext()); @@ -472,16 +483,6 @@ /** * - * @return the bean that defines emailScheduleMessageJob. - */ - private JobDetail getEmailScheduleMessageJob() { - WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServlet() - .getServletContext()); - return (JobDetail) ctx.getBean(MonitoringConstants.JOB_EMAIL_MESSAGE); - } - - /** - * * @return the bean that defines Scheduler. */ private Scheduler getScheduler() {