0001: /*
0002: * Copyright 2004-2005 OpenSymphony
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005: * use this file except in compliance with the License. You may obtain a copy
0006: * of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013: * License for the specific language governing permissions and limitations
0014: * under the License.
0015: *
0016: */
0017:
0018: /*
0019: * Previously Copyright (c) 2001-2004 James House
0020: */
0021: package org.quartz.simpl;
0022:
0023: import java.util.ArrayList;
0024: import java.util.Comparator;
0025: import java.util.Date;
0026: import java.util.HashMap;
0027: import java.util.HashSet;
0028: import java.util.Iterator;
0029: import java.util.Set;
0030: import java.util.TreeSet;
0031:
0032: import org.apache.commons.logging.Log;
0033: import org.apache.commons.logging.LogFactory;
0034: import org.quartz.Calendar;
0035: import org.quartz.JobDataMap;
0036: import org.quartz.JobDetail;
0037: import org.quartz.JobPersistenceException;
0038: import org.quartz.ObjectAlreadyExistsException;
0039: import org.quartz.SchedulerException;
0040: import org.quartz.Trigger;
0041: import org.quartz.core.SchedulingContext;
0042: import org.quartz.spi.ClassLoadHelper;
0043: import org.quartz.spi.JobStore;
0044: import org.quartz.spi.SchedulerSignaler;
0045: import org.quartz.spi.TriggerFiredBundle;
0046:
0047: /**
0048: * <p>
0049: * This class implements a <code>{@link org.quartz.spi.JobStore}</code> that
0050: * utilizes RAM as its storage device.
0051: * </p>
0052: *
0053: * <p>
0054: * As you should know, the ramification of this is that access is extrememly
0055: * fast, but the data is completely volatile - therefore this <code>JobStore</code>
0056: * should not be used if true persistence between program shutdowns is
0057: * required.
0058: * </p>
0059: *
0060: * @author James House
0061: * @author Sharada Jambula
0062: * @author Eric Mueller
0063: */
0064: public class RAMJobStore implements JobStore {
0065:
0066: /*
0067: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0068: *
0069: * Data members.
0070: *
0071: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0072: */
0073:
0074: protected HashMap jobsByFQN = new HashMap(1000);
0075:
0076: protected HashMap triggersByFQN = new HashMap(1000);
0077:
0078: protected HashMap jobsByGroup = new HashMap(25);
0079:
0080: protected HashMap triggersByGroup = new HashMap(25);
0081:
0082: protected TreeSet timeTriggers = new TreeSet(
0083: new TriggerComparator());
0084:
0085: protected HashMap calendarsByName = new HashMap(25);
0086:
0087: protected ArrayList triggers = new ArrayList(1000);
0088:
0089: protected final Object jobLock = new Object();
0090:
0091: protected final Object triggerLock = new Object();
0092:
0093: protected HashSet pausedTriggerGroups = new HashSet();
0094:
0095: protected HashSet blockedJobs = new HashSet();
0096:
0097: protected long misfireThreshold = 5000l;
0098:
0099: protected SchedulerSignaler signaler;
0100:
0101: private final Log log = LogFactory.getLog(getClass());
0102:
0103: /*
0104: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0105: *
0106: * Constructors.
0107: *
0108: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0109: */
0110:
0111: /**
0112: * <p>
0113: * Create a new <code>RAMJobStore</code>.
0114: * </p>
0115: */
0116: public RAMJobStore() {
0117: }
0118:
0119: /*
0120: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0121: *
0122: * Interface.
0123: *
0124: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0125: */
0126:
0127: protected Log getLog() {
0128: return log;
0129: }
0130:
0131: /**
0132: * <p>
0133: * Called by the QuartzScheduler before the <code>JobStore</code> is
0134: * used, in order to give the it a chance to initialize.
0135: * </p>
0136: */
0137: public void initialize(ClassLoadHelper loadHelper,
0138: SchedulerSignaler signaler) {
0139:
0140: this .signaler = signaler;
0141:
0142: getLog().info("RAMJobStore initialized.");
0143: }
0144:
0145: public void schedulerStarted() throws SchedulerException {
0146: // nothing to do
0147: }
0148:
0149: public long getMisfireThreshold() {
0150: return misfireThreshold;
0151: }
0152:
0153: /**
0154: * The number of milliseconds by which a trigger must have missed its
0155: * next-fire-time, in order for it to be considered "misfired" and thus
0156: * have its misfire instruction applied.
0157: *
0158: * @param misfireThreshold
0159: */
0160: public void setMisfireThreshold(long misfireThreshold) {
0161: if (misfireThreshold < 1) {
0162: throw new IllegalArgumentException(
0163: "Misfirethreashold must be larger than 0");
0164: }
0165: this .misfireThreshold = misfireThreshold;
0166: }
0167:
0168: /**
0169: * <p>
0170: * Called by the QuartzScheduler to inform the <code>JobStore</code> that
0171: * it should free up all of it's resources because the scheduler is
0172: * shutting down.
0173: * </p>
0174: */
0175: public void shutdown() {
0176: }
0177:
0178: public boolean supportsPersistence() {
0179: return false;
0180: }
0181:
0182: /**
0183: * <p>
0184: * Store the given <code>{@link org.quartz.JobDetail}</code> and <code>{@link org.quartz.Trigger}</code>.
0185: * </p>
0186: *
0187: * @param newJob
0188: * The <code>JobDetail</code> to be stored.
0189: * @param newTrigger
0190: * The <code>Trigger</code> to be stored.
0191: * @throws ObjectAlreadyExistsException
0192: * if a <code>Job</code> with the same name/group already
0193: * exists.
0194: */
0195: public void storeJobAndTrigger(SchedulingContext ctxt,
0196: JobDetail newJob, Trigger newTrigger)
0197: throws JobPersistenceException {
0198: storeJob(ctxt, newJob, false);
0199: storeTrigger(ctxt, newTrigger, false);
0200: }
0201:
0202: /**
0203: * <p>
0204: * Store the given <code>{@link org.quartz.Job}</code>.
0205: * </p>
0206: *
0207: * @param newJob
0208: * The <code>Job</code> to be stored.
0209: * @param replaceExisting
0210: * If <code>true</code>, any <code>Job</code> existing in the
0211: * <code>JobStore</code> with the same name & group should be
0212: * over-written.
0213: * @throws ObjectAlreadyExistsException
0214: * if a <code>Job</code> with the same name/group already
0215: * exists, and replaceExisting is set to false.
0216: */
0217: public void storeJob(SchedulingContext ctxt, JobDetail newJob,
0218: boolean replaceExisting)
0219: throws ObjectAlreadyExistsException {
0220: JobWrapper jw = new JobWrapper((JobDetail) newJob.clone());
0221:
0222: boolean repl = false;
0223:
0224: if (jobsByFQN.get(jw.key) != null) {
0225: if (!replaceExisting) {
0226: throw new ObjectAlreadyExistsException(newJob);
0227: }
0228: repl = true;
0229: }
0230:
0231: synchronized (jobLock) {
0232: if (!repl) {
0233: // get job group
0234: HashMap grpMap = (HashMap) jobsByGroup.get(newJob
0235: .getGroup());
0236: if (grpMap == null) {
0237: grpMap = new HashMap(100);
0238: jobsByGroup.put(newJob.getGroup(), grpMap);
0239: }
0240: // add to jobs by group
0241: grpMap.put(newJob.getName(), jw);
0242: // add to jobs by FQN map
0243: jobsByFQN.put(jw.key, jw);
0244: } else {
0245: // update job detail
0246: JobWrapper orig = (JobWrapper) jobsByFQN.get(jw.key);
0247: orig.jobDetail = jw.jobDetail; // already cloned
0248: }
0249: }
0250: }
0251:
0252: /**
0253: * <p>
0254: * Remove (delete) the <code>{@link org.quartz.Job}</code> with the given
0255: * name, and any <code>{@link org.quartz.Trigger}</code> s that reference
0256: * it.
0257: * </p>
0258: *
0259: * @param jobName
0260: * The name of the <code>Job</code> to be removed.
0261: * @param groupName
0262: * The group name of the <code>Job</code> to be removed.
0263: * @return <code>true</code> if a <code>Job</code> with the given name &
0264: * group was found and removed from the store.
0265: */
0266: public boolean removeJob(SchedulingContext ctxt, String jobName,
0267: String groupName) {
0268: String key = JobWrapper.getJobNameKey(jobName, groupName);
0269:
0270: boolean found = false;
0271:
0272: Trigger[] trigger = getTriggersForJob(ctxt, jobName, groupName);
0273: for (int i = 0; i < trigger.length; i++) {
0274: Trigger trig = trigger[i];
0275: this .removeTrigger(ctxt, trig.getName(), trig.getGroup());
0276: found = true;
0277: }
0278: synchronized (jobLock) {
0279: found = (jobsByFQN.remove(key) != null) | found;
0280: if (found) {
0281:
0282: HashMap grpMap = (HashMap) jobsByGroup.get(groupName);
0283: if (grpMap != null) {
0284: grpMap.remove(jobName);
0285: if (grpMap.size() == 0) {
0286: jobsByGroup.remove(groupName);
0287: }
0288: }
0289: }
0290: }
0291:
0292: return found;
0293: }
0294:
0295: /**
0296: * <p>
0297: * Store the given <code>{@link org.quartz.Trigger}</code>.
0298: * </p>
0299: *
0300: * @param newTrigger
0301: * The <code>Trigger</code> to be stored.
0302: * @param replaceExisting
0303: * If <code>true</code>, any <code>Trigger</code> existing in
0304: * the <code>JobStore</code> with the same name & group should
0305: * be over-written.
0306: * @throws ObjectAlreadyExistsException
0307: * if a <code>Trigger</code> with the same name/group already
0308: * exists, and replaceExisting is set to false.
0309: *
0310: * @see #pauseTriggerGroup(SchedulingContext, String)
0311: */
0312: public void storeTrigger(SchedulingContext ctxt,
0313: Trigger newTrigger, boolean replaceExisting)
0314: throws JobPersistenceException {
0315: TriggerWrapper tw = new TriggerWrapper((Trigger) newTrigger
0316: .clone());
0317:
0318: if (triggersByFQN.get(tw.key) != null) {
0319: if (!replaceExisting) {
0320: throw new ObjectAlreadyExistsException(newTrigger);
0321: }
0322:
0323: removeTrigger(ctxt, newTrigger.getName(), newTrigger
0324: .getGroup());
0325: }
0326:
0327: if (retrieveJob(ctxt, newTrigger.getJobName(), newTrigger
0328: .getJobGroup()) == null) {
0329: throw new JobPersistenceException("The job ("
0330: + newTrigger.getFullJobName()
0331: + ") referenced by the trigger does not exist.");
0332: }
0333:
0334: synchronized (triggerLock) {
0335: // add to triggers array
0336: triggers.add(tw);
0337: // add to triggers by group
0338: HashMap grpMap = (HashMap) triggersByGroup.get(newTrigger
0339: .getGroup());
0340: if (grpMap == null) {
0341: grpMap = new HashMap(100);
0342: triggersByGroup.put(newTrigger.getGroup(), grpMap);
0343: }
0344: grpMap.put(newTrigger.getName(), tw);
0345: // add to triggers by FQN map
0346: triggersByFQN.put(tw.key, tw);
0347:
0348: synchronized (pausedTriggerGroups) {
0349: if (pausedTriggerGroups.contains(newTrigger.getGroup())) {
0350: tw.state = TriggerWrapper.STATE_PAUSED;
0351: if (blockedJobs.contains(tw.jobKey)) {
0352: tw.state = TriggerWrapper.STATE_PAUSED_BLOCKED;
0353: }
0354: } else if (blockedJobs.contains(tw.jobKey)) {
0355: tw.state = TriggerWrapper.STATE_BLOCKED;
0356: } else {
0357: timeTriggers.add(tw);
0358: }
0359: }
0360: }
0361: }
0362:
0363: /**
0364: * <p>
0365: * Remove (delete) the <code>{@link org.quartz.Trigger}</code> with the
0366: * given name.
0367: * </p>
0368: *
0369: * @param triggerName
0370: * The name of the <code>Trigger</code> to be removed.
0371: * @param groupName
0372: * The group name of the <code>Trigger</code> to be removed.
0373: * @return <code>true</code> if a <code>Trigger</code> with the given
0374: * name & group was found and removed from the store.
0375: */
0376: public boolean removeTrigger(SchedulingContext ctxt,
0377: String triggerName, String groupName) {
0378: String key = TriggerWrapper.getTriggerNameKey(triggerName,
0379: groupName);
0380:
0381: boolean found = false;
0382:
0383: synchronized (triggerLock) {
0384: // remove from triggers by FQN map
0385: found = (triggersByFQN.remove(key) == null) ? false : true;
0386: if (found) {
0387: TriggerWrapper tw = null;
0388: // remove from triggers by group
0389: HashMap grpMap = (HashMap) triggersByGroup
0390: .get(groupName);
0391: if (grpMap != null) {
0392: grpMap.remove(triggerName);
0393: if (grpMap.size() == 0) {
0394: triggersByGroup.remove(groupName);
0395: }
0396: }
0397: // remove from triggers array
0398: Iterator tgs = triggers.iterator();
0399: while (tgs.hasNext()) {
0400: tw = (TriggerWrapper) tgs.next();
0401: if (key.equals(tw.key)) {
0402: tgs.remove();
0403: break;
0404: }
0405: }
0406: timeTriggers.remove(tw);
0407:
0408: JobWrapper jw = (JobWrapper) jobsByFQN.get(JobWrapper
0409: .getJobNameKey(tw.trigger.getJobName(),
0410: tw.trigger.getJobGroup()));
0411: Trigger[] trigs = getTriggersForJob(ctxt, tw.trigger
0412: .getJobName(), tw.trigger.getJobGroup());
0413: if ((trigs == null || trigs.length == 0)
0414: && !jw.jobDetail.isDurable()) {
0415: removeJob(ctxt, tw.trigger.getJobName(), tw.trigger
0416: .getJobGroup());
0417: }
0418: }
0419: }
0420:
0421: return found;
0422: }
0423:
0424: /**
0425: * @see org.quartz.spi.JobStore#replaceTrigger(org.quartz.core.SchedulingContext, java.lang.String, java.lang.String, org.quartz.Trigger)
0426: */
0427: public boolean replaceTrigger(SchedulingContext ctxt,
0428: String triggerName, String groupName, Trigger newTrigger)
0429: throws JobPersistenceException {
0430: String key = TriggerWrapper.getTriggerNameKey(triggerName,
0431: groupName);
0432:
0433: boolean found = false;
0434:
0435: synchronized (triggerLock) {
0436: // remove from triggers by FQN map
0437: TriggerWrapper tw = (TriggerWrapper) triggersByFQN
0438: .remove(key);
0439: found = (tw == null) ? false : true;
0440:
0441: if (found) {
0442:
0443: if (!tw.getTrigger().getJobName().equals(
0444: newTrigger.getJobName())
0445: || !tw.getTrigger().getJobGroup().equals(
0446: newTrigger.getJobGroup())) {
0447: throw new JobPersistenceException(
0448: "New trigger is not related to the same job as the old trigger.");
0449: }
0450:
0451: tw = null;
0452: // remove from triggers by group
0453: HashMap grpMap = (HashMap) triggersByGroup
0454: .get(groupName);
0455: if (grpMap != null) {
0456: grpMap.remove(triggerName);
0457: if (grpMap.size() == 0) {
0458: triggersByGroup.remove(groupName);
0459: }
0460: }
0461: // remove from triggers array
0462: Iterator tgs = triggers.iterator();
0463: while (tgs.hasNext()) {
0464: tw = (TriggerWrapper) tgs.next();
0465: if (key.equals(tw.key)) {
0466: tgs.remove();
0467: break;
0468: }
0469: }
0470: timeTriggers.remove(tw);
0471:
0472: try {
0473: storeTrigger(ctxt, newTrigger, false);
0474: } catch (JobPersistenceException jpe) {
0475: storeTrigger(ctxt, tw.getTrigger(), false); // put previous trigger back...
0476: throw jpe;
0477: }
0478: }
0479: }
0480:
0481: return found;
0482: }
0483:
0484: /**
0485: * <p>
0486: * Retrieve the <code>{@link org.quartz.JobDetail}</code> for the given
0487: * <code>{@link org.quartz.Job}</code>.
0488: * </p>
0489: *
0490: * @param jobName
0491: * The name of the <code>Job</code> to be retrieved.
0492: * @param groupName
0493: * The group name of the <code>Job</code> to be retrieved.
0494: * @return The desired <code>Job</code>, or null if there is no match.
0495: */
0496: public JobDetail retrieveJob(SchedulingContext ctxt,
0497: String jobName, String groupName) {
0498: JobWrapper jw = (JobWrapper) jobsByFQN.get(JobWrapper
0499: .getJobNameKey(jobName, groupName));
0500:
0501: return (jw != null) ? (JobDetail) jw.jobDetail.clone() : null;
0502: }
0503:
0504: /**
0505: * <p>
0506: * Retrieve the given <code>{@link org.quartz.Trigger}</code>.
0507: * </p>
0508: *
0509: * @param triggerName
0510: * The name of the <code>Trigger</code> to be retrieved.
0511: * @param groupName
0512: * The group name of the <code>Trigger</code> to be retrieved.
0513: * @return The desired <code>Trigger</code>, or null if there is no
0514: * match.
0515: */
0516: public Trigger retrieveTrigger(SchedulingContext ctxt,
0517: String triggerName, String groupName) {
0518: TriggerWrapper tw = (TriggerWrapper) triggersByFQN
0519: .get(TriggerWrapper.getTriggerNameKey(triggerName,
0520: groupName));
0521:
0522: return (tw != null) ? (Trigger) tw.getTrigger().clone() : null;
0523: }
0524:
0525: /**
0526: * <p>
0527: * Get the current state of the identified <code>{@link Trigger}</code>.
0528: * </p>
0529: *
0530: * @see Trigger#STATE_NORMAL
0531: * @see Trigger#STATE_PAUSED
0532: * @see Trigger#STATE_COMPLETE
0533: * @see Trigger#STATE_ERROR
0534: * @see Trigger#STATE_BLOCKED
0535: * @see Trigger#STATE_NONE
0536: */
0537: public int getTriggerState(SchedulingContext ctxt,
0538: String triggerName, String groupName)
0539: throws JobPersistenceException {
0540: TriggerWrapper tw = (TriggerWrapper) triggersByFQN
0541: .get(TriggerWrapper.getTriggerNameKey(triggerName,
0542: groupName));
0543: if (tw == null) {
0544: return Trigger.STATE_NONE;
0545: }
0546:
0547: if (tw.state == TriggerWrapper.STATE_COMPLETE) {
0548: return Trigger.STATE_COMPLETE;
0549: }
0550:
0551: if (tw.state == TriggerWrapper.STATE_PAUSED) {
0552: return Trigger.STATE_PAUSED;
0553: }
0554:
0555: if (tw.state == TriggerWrapper.STATE_PAUSED_BLOCKED) {
0556: return Trigger.STATE_PAUSED;
0557: }
0558:
0559: if (tw.state == TriggerWrapper.STATE_BLOCKED) {
0560: return Trigger.STATE_BLOCKED;
0561: }
0562:
0563: if (tw.state == TriggerWrapper.STATE_ERROR) {
0564: return Trigger.STATE_ERROR;
0565: }
0566:
0567: return Trigger.STATE_NORMAL;
0568: }
0569:
0570: /**
0571: * <p>
0572: * Store the given <code>{@link org.quartz.Calendar}</code>.
0573: * </p>
0574: *
0575: * @param calendar
0576: * The <code>Calendar</code> to be stored.
0577: * @param replaceExisting
0578: * If <code>true</code>, any <code>Calendar</code> existing
0579: * in the <code>JobStore</code> with the same name & group
0580: * should be over-written.
0581: * @param updateTriggers
0582: * If <code>true</code>, any <code>Trigger</code>s existing
0583: * in the <code>JobStore</code> that reference an existing
0584: * Calendar with the same name with have their next fire time
0585: * re-computed with the new <code>Calendar</code>.
0586: * @throws ObjectAlreadyExistsException
0587: * if a <code>Calendar</code> with the same name already
0588: * exists, and replaceExisting is set to false.
0589: */
0590: public void storeCalendar(SchedulingContext ctxt, String name,
0591: Calendar calendar, boolean replaceExisting,
0592: boolean updateTriggers) throws ObjectAlreadyExistsException {
0593: Object obj = calendarsByName.get(name);
0594:
0595: if (obj != null && replaceExisting == false) {
0596: throw new ObjectAlreadyExistsException(
0597: "Calendar with name '" + name + "' already exists.");
0598: } else if (obj != null) {
0599: calendarsByName.remove(name);
0600: }
0601:
0602: calendarsByName.put(name, calendar);
0603:
0604: if (obj != null && updateTriggers) {
0605: synchronized (triggerLock) {
0606: Iterator trigs = getTriggerWrappersForCalendar(name)
0607: .iterator();
0608: while (trigs.hasNext()) {
0609: TriggerWrapper tw = (TriggerWrapper) trigs.next();
0610: Trigger trig = tw.getTrigger();
0611: boolean removed = timeTriggers.remove(tw);
0612:
0613: trig.updateWithNewCalendar(calendar,
0614: getMisfireThreshold());
0615:
0616: if (removed) {
0617: timeTriggers.add(tw);
0618: }
0619: }
0620: }
0621: }
0622: }
0623:
0624: /**
0625: * <p>
0626: * Remove (delete) the <code>{@link org.quartz.Calendar}</code> with the
0627: * given name.
0628: * </p>
0629: *
0630: * <p>
0631: * If removal of the <code>Calendar</code> would result in
0632: * <code.Trigger</code>s pointing to non-existent calendars, then a
0633: * <code>JobPersistenceException</code> will be thrown.</p>
0634: * *
0635: * @param calName The name of the <code>Calendar</code> to be removed.
0636: * @return <code>true</code> if a <code>Calendar</code> with the given name
0637: * was found and removed from the store.
0638: */
0639: public boolean removeCalendar(SchedulingContext ctxt, String calName)
0640: throws JobPersistenceException {
0641: int numRefs = 0;
0642:
0643: synchronized (triggerLock) {
0644: Iterator itr = triggers.iterator();
0645: while (itr.hasNext()) {
0646: Trigger trigg = ((TriggerWrapper) itr.next()).trigger;
0647: if (trigg.getCalendarName() != null
0648: && trigg.getCalendarName().equals(calName)) {
0649: numRefs++;
0650: }
0651: }
0652: }
0653:
0654: if (numRefs > 0) {
0655: throw new JobPersistenceException(
0656: "Calender cannot be removed if it referenced by a Trigger!");
0657: }
0658:
0659: return (calendarsByName.remove(calName) != null);
0660: }
0661:
0662: /**
0663: * <p>
0664: * Retrieve the given <code>{@link org.quartz.Trigger}</code>.
0665: * </p>
0666: *
0667: * @param calName
0668: * The name of the <code>Calendar</code> to be retrieved.
0669: * @return The desired <code>Calendar</code>, or null if there is no
0670: * match.
0671: */
0672: public Calendar retrieveCalendar(SchedulingContext ctxt,
0673: String calName) {
0674: return (Calendar) calendarsByName.get(calName);
0675: }
0676:
0677: /**
0678: * <p>
0679: * Get the number of <code>{@link org.quartz.JobDetail}</code> s that are
0680: * stored in the <code>JobsStore</code>.
0681: * </p>
0682: */
0683: public int getNumberOfJobs(SchedulingContext ctxt) {
0684: return jobsByFQN.size();
0685: }
0686:
0687: /**
0688: * <p>
0689: * Get the number of <code>{@link org.quartz.Trigger}</code> s that are
0690: * stored in the <code>JobsStore</code>.
0691: * </p>
0692: */
0693: public int getNumberOfTriggers(SchedulingContext ctxt) {
0694: return triggers.size();
0695: }
0696:
0697: /**
0698: * <p>
0699: * Get the number of <code>{@link org.quartz.Calendar}</code> s that are
0700: * stored in the <code>JobsStore</code>.
0701: * </p>
0702: */
0703: public int getNumberOfCalendars(SchedulingContext ctxt) {
0704: return calendarsByName.size();
0705: }
0706:
0707: /**
0708: * <p>
0709: * Get the names of all of the <code>{@link org.quartz.Job}</code> s that
0710: * have the given group name.
0711: * </p>
0712: */
0713: public String[] getJobNames(SchedulingContext ctxt, String groupName) {
0714: String[] outList = null;
0715: HashMap grpMap = (HashMap) jobsByGroup.get(groupName);
0716: if (grpMap != null) {
0717: synchronized (jobLock) {
0718: outList = new String[grpMap.size()];
0719: int outListPos = 0;
0720:
0721: for (Iterator valueIter = grpMap.values().iterator(); valueIter
0722: .hasNext();) {
0723: JobWrapper jw = (JobWrapper) valueIter.next();
0724:
0725: if (jw != null) {
0726: outList[outListPos++] = jw.jobDetail.getName();
0727: }
0728: }
0729: }
0730: } else {
0731: outList = new String[0];
0732: }
0733:
0734: return outList;
0735: }
0736:
0737: /**
0738: * <p>
0739: * Get the names of all of the <code>{@link org.quartz.Calendar}</code> s
0740: * in the <code>JobStore</code>.
0741: * </p>
0742: *
0743: * <p>
0744: * If there are no Calendars in the given group name, the result should be
0745: * a zero-length array (not <code>null</code>).
0746: * </p>
0747: */
0748: public String[] getCalendarNames(SchedulingContext ctxt) {
0749: Set names = calendarsByName.keySet();
0750: return (String[]) names.toArray(new String[names.size()]);
0751: }
0752:
0753: /**
0754: * <p>
0755: * Get the names of all of the <code>{@link org.quartz.Trigger}</code> s
0756: * that have the given group name.
0757: * </p>
0758: */
0759: public String[] getTriggerNames(SchedulingContext ctxt,
0760: String groupName) {
0761: String[] outList = null;
0762: HashMap grpMap = (HashMap) triggersByGroup.get(groupName);
0763: if (grpMap != null) {
0764: synchronized (triggerLock) {
0765: outList = new String[grpMap.size()];
0766: int outListPos = 0;
0767:
0768: for (Iterator valueIter = grpMap.values().iterator(); valueIter
0769: .hasNext();) {
0770: TriggerWrapper tw = (TriggerWrapper) valueIter
0771: .next();
0772:
0773: if (tw != null) {
0774: outList[outListPos++] = tw.trigger.getName();
0775: }
0776: }
0777: }
0778: } else {
0779: outList = new String[0];
0780: }
0781:
0782: return outList;
0783: }
0784:
0785: /**
0786: * <p>
0787: * Get the names of all of the <code>{@link org.quartz.Job}</code>
0788: * groups.
0789: * </p>
0790: */
0791: public String[] getJobGroupNames(SchedulingContext ctxt) {
0792: String[] outList = null;
0793:
0794: synchronized (jobLock) {
0795: outList = new String[jobsByGroup.size()];
0796: int outListPos = 0;
0797: Iterator keys = jobsByGroup.keySet().iterator();
0798: while (keys.hasNext()) {
0799: outList[outListPos++] = (String) keys.next();
0800: }
0801: }
0802:
0803: return outList;
0804: }
0805:
0806: /**
0807: * <p>
0808: * Get the names of all of the <code>{@link org.quartz.Trigger}</code>
0809: * groups.
0810: * </p>
0811: */
0812: public String[] getTriggerGroupNames(SchedulingContext ctxt) {
0813: String[] outList = null;
0814:
0815: synchronized (triggerLock) {
0816: outList = new String[triggersByGroup.size()];
0817: int outListPos = 0;
0818: Iterator keys = triggersByGroup.keySet().iterator();
0819: while (keys.hasNext()) {
0820: outList[outListPos++] = (String) keys.next();
0821: }
0822: }
0823:
0824: return outList;
0825: }
0826:
0827: /**
0828: * <p>
0829: * Get all of the Triggers that are associated to the given Job.
0830: * </p>
0831: *
0832: * <p>
0833: * If there are no matches, a zero-length array should be returned.
0834: * </p>
0835: */
0836: public Trigger[] getTriggersForJob(SchedulingContext ctxt,
0837: String jobName, String groupName) {
0838: ArrayList trigList = new ArrayList();
0839:
0840: String jobKey = JobWrapper.getJobNameKey(jobName, groupName);
0841: synchronized (triggerLock) {
0842: for (int i = 0; i < triggers.size(); i++) {
0843: TriggerWrapper tw = (TriggerWrapper) triggers.get(i);
0844: if (tw.jobKey.equals(jobKey)) {
0845: trigList.add(tw.trigger.clone());
0846: }
0847: }
0848: }
0849:
0850: return (Trigger[]) trigList
0851: .toArray(new Trigger[trigList.size()]);
0852: }
0853:
0854: protected ArrayList getTriggerWrappersForJob(String jobName,
0855: String groupName) {
0856: ArrayList trigList = new ArrayList();
0857:
0858: String jobKey = JobWrapper.getJobNameKey(jobName, groupName);
0859: synchronized (triggerLock) {
0860: for (int i = 0; i < triggers.size(); i++) {
0861: TriggerWrapper tw = (TriggerWrapper) triggers.get(i);
0862: if (tw.jobKey.equals(jobKey)) {
0863: trigList.add(tw);
0864: }
0865: }
0866: }
0867:
0868: return trigList;
0869: }
0870:
0871: protected ArrayList getTriggerWrappersForCalendar(String calName) {
0872: ArrayList trigList = new ArrayList();
0873:
0874: synchronized (triggerLock) {
0875: for (int i = 0; i < triggers.size(); i++) {
0876: TriggerWrapper tw = (TriggerWrapper) triggers.get(i);
0877: String tcalName = tw.getTrigger().getCalendarName();
0878: if (tcalName != null && tcalName.equals(calName)) {
0879: trigList.add(tw);
0880: }
0881: }
0882: }
0883:
0884: return trigList;
0885: }
0886:
0887: /**
0888: * <p>
0889: * Pause the <code>{@link Trigger}</code> with the given name.
0890: * </p>
0891: *
0892: */
0893: public void pauseTrigger(SchedulingContext ctxt,
0894: String triggerName, String groupName) {
0895:
0896: TriggerWrapper tw = (TriggerWrapper) triggersByFQN
0897: .get(TriggerWrapper.getTriggerNameKey(triggerName,
0898: groupName));
0899:
0900: // does the trigger exist?
0901: if (tw == null || tw.trigger == null) {
0902: return;
0903: }
0904:
0905: // if the trigger is "complete" pausing it does not make sense...
0906: if (tw.state == TriggerWrapper.STATE_COMPLETE) {
0907: return;
0908: }
0909:
0910: synchronized (triggerLock) {
0911: if (tw.state == TriggerWrapper.STATE_BLOCKED) {
0912: tw.state = TriggerWrapper.STATE_PAUSED_BLOCKED;
0913: } else {
0914: tw.state = TriggerWrapper.STATE_PAUSED;
0915: }
0916:
0917: timeTriggers.remove(tw);
0918: }
0919: }
0920:
0921: /**
0922: * <p>
0923: * Pause all of the <code>{@link Trigger}s</code> in the given group.
0924: * </p>
0925: *
0926: * <p>
0927: * The JobStore should "remember" that the group is paused, and impose the
0928: * pause on any new triggers that are added to the group while the group is
0929: * paused.
0930: * </p>
0931: *
0932: */
0933: public void pauseTriggerGroup(SchedulingContext ctxt,
0934: String groupName) {
0935:
0936: synchronized (pausedTriggerGroups) {
0937: if (pausedTriggerGroups.contains(groupName)) {
0938: return;
0939: }
0940:
0941: pausedTriggerGroups.add(groupName);
0942: String[] names = getTriggerNames(ctxt, groupName);
0943:
0944: for (int i = 0; i < names.length; i++) {
0945: pauseTrigger(ctxt, names[i], groupName);
0946: }
0947: }
0948: }
0949:
0950: /**
0951: * <p>
0952: * Pause the <code>{@link org.quartz.JobDetail}</code> with the given
0953: * name - by pausing all of its current <code>Trigger</code>s.
0954: * </p>
0955: *
0956: */
0957: public void pauseJob(SchedulingContext ctxt, String jobName,
0958: String groupName) {
0959: synchronized (pausedTriggerGroups) {
0960: Trigger[] triggers = getTriggersForJob(ctxt, jobName,
0961: groupName);
0962: for (int j = 0; j < triggers.length; j++) {
0963: pauseTrigger(ctxt, triggers[j].getName(), triggers[j]
0964: .getGroup());
0965: }
0966: }
0967: }
0968:
0969: /**
0970: * <p>
0971: * Pause all of the <code>{@link org.quartz.JobDetail}s</code> in the
0972: * given group - by pausing all of their <code>Trigger</code>s.
0973: * </p>
0974: *
0975: *
0976: * <p>
0977: * The JobStore should "remember" that the group is paused, and impose the
0978: * pause on any new jobs that are added to the group while the group is
0979: * paused.
0980: * </p>
0981: */
0982: public void pauseJobGroup(SchedulingContext ctxt, String groupName) {
0983: synchronized (pausedTriggerGroups) {
0984: String[] jobNames = getJobNames(ctxt, groupName);
0985:
0986: for (int i = 0; i < jobNames.length; i++) {
0987: Trigger[] triggers = getTriggersForJob(ctxt,
0988: jobNames[i], groupName);
0989: for (int j = 0; j < triggers.length; j++) {
0990: pauseTrigger(ctxt, triggers[j].getName(),
0991: triggers[j].getGroup());
0992: }
0993: }
0994: }
0995: }
0996:
0997: /**
0998: * <p>
0999: * Resume (un-pause) the <code>{@link Trigger}</code> with the given
1000: * name.
1001: * </p>
1002: *
1003: * <p>
1004: * If the <code>Trigger</code> missed one or more fire-times, then the
1005: * <code>Trigger</code>'s misfire instruction will be applied.
1006: * </p>
1007: *
1008: */
1009: public void resumeTrigger(SchedulingContext ctxt,
1010: String triggerName, String groupName) {
1011:
1012: TriggerWrapper tw = (TriggerWrapper) triggersByFQN
1013: .get(TriggerWrapper.getTriggerNameKey(triggerName,
1014: groupName));
1015:
1016: Trigger trig = tw.getTrigger();
1017:
1018: // does the trigger exist?
1019: if (tw == null || tw.trigger == null) {
1020: return;
1021: }
1022: // if the trigger is not paused resuming it does not make sense...
1023: if (tw.state != TriggerWrapper.STATE_PAUSED
1024: && tw.state != TriggerWrapper.STATE_PAUSED_BLOCKED) {
1025: return;
1026: }
1027:
1028: synchronized (triggerLock) {
1029: if (blockedJobs.contains(JobWrapper.getJobNameKey(trig
1030: .getJobName(), trig.getJobGroup()))) {
1031: tw.state = TriggerWrapper.STATE_BLOCKED;
1032: } else {
1033: tw.state = TriggerWrapper.STATE_WAITING;
1034: }
1035:
1036: applyMisfire(tw);
1037:
1038: if (tw.state == TriggerWrapper.STATE_WAITING) {
1039: timeTriggers.add(tw);
1040: }
1041: }
1042: }
1043:
1044: /**
1045: * <p>
1046: * Resume (un-pause) all of the <code>{@link Trigger}s</code> in the
1047: * given group.
1048: * </p>
1049: *
1050: * <p>
1051: * If any <code>Trigger</code> missed one or more fire-times, then the
1052: * <code>Trigger</code>'s misfire instruction will be applied.
1053: * </p>
1054: *
1055: */
1056: public void resumeTriggerGroup(SchedulingContext ctxt,
1057: String groupName) {
1058:
1059: synchronized (pausedTriggerGroups) {
1060: String[] names = getTriggerNames(ctxt, groupName);
1061:
1062: for (int i = 0; i < names.length; i++) {
1063: resumeTrigger(ctxt, names[i], groupName);
1064: }
1065: pausedTriggerGroups.remove(groupName);
1066: }
1067: }
1068:
1069: /**
1070: * <p>
1071: * Resume (un-pause) the <code>{@link org.quartz.JobDetail}</code> with
1072: * the given name.
1073: * </p>
1074: *
1075: * <p>
1076: * If any of the <code>Job</code>'s<code>Trigger</code> s missed one
1077: * or more fire-times, then the <code>Trigger</code>'s misfire
1078: * instruction will be applied.
1079: * </p>
1080: *
1081: */
1082: public void resumeJob(SchedulingContext ctxt, String jobName,
1083: String groupName) {
1084:
1085: synchronized (pausedTriggerGroups) {
1086: Trigger[] triggers = getTriggersForJob(ctxt, jobName,
1087: groupName);
1088: for (int j = 0; j < triggers.length; j++) {
1089: resumeTrigger(ctxt, triggers[j].getName(), triggers[j]
1090: .getGroup());
1091: }
1092: }
1093: }
1094:
1095: /**
1096: * <p>
1097: * Resume (un-pause) all of the <code>{@link org.quartz.JobDetail}s</code>
1098: * in the given group.
1099: * </p>
1100: *
1101: * <p>
1102: * If any of the <code>Job</code> s had <code>Trigger</code> s that
1103: * missed one or more fire-times, then the <code>Trigger</code>'s
1104: * misfire instruction will be applied.
1105: * </p>
1106: *
1107: */
1108: public void resumeJobGroup(SchedulingContext ctxt, String groupName) {
1109: synchronized (pausedTriggerGroups) {
1110: String[] jobNames = getJobNames(ctxt, groupName);
1111:
1112: for (int i = 0; i < jobNames.length; i++) {
1113: Trigger[] triggers = getTriggersForJob(ctxt,
1114: jobNames[i], groupName);
1115: for (int j = 0; j < triggers.length; j++) {
1116: resumeTrigger(ctxt, triggers[j].getName(),
1117: triggers[j].getGroup());
1118: }
1119: }
1120: }
1121: }
1122:
1123: /**
1124: * <p>
1125: * Pause all triggers - equivalent of calling <code>pauseTriggerGroup(group)</code>
1126: * on every group.
1127: * </p>
1128: *
1129: * <p>
1130: * When <code>resumeAll()</code> is called (to un-pause), trigger misfire
1131: * instructions WILL be applied.
1132: * </p>
1133: *
1134: * @see #resumeAll(SchedulingContext)
1135: * @see #pauseTriggerGroup(SchedulingContext, String)
1136: */
1137: public void pauseAll(SchedulingContext ctxt) {
1138:
1139: synchronized (pausedTriggerGroups) {
1140: String[] names = getTriggerGroupNames(ctxt);
1141:
1142: for (int i = 0; i < names.length; i++) {
1143: pauseTriggerGroup(ctxt, names[i]);
1144: }
1145: }
1146: }
1147:
1148: /**
1149: * <p>
1150: * Resume (un-pause) all triggers - equivalent of calling <code>resumeTriggerGroup(group)</code>
1151: * on every group.
1152: * </p>
1153: *
1154: * <p>
1155: * If any <code>Trigger</code> missed one or more fire-times, then the
1156: * <code>Trigger</code>'s misfire instruction will be applied.
1157: * </p>
1158: *
1159: * @see #pauseAll(SchedulingContext)
1160: */
1161: public void resumeAll(SchedulingContext ctxt) {
1162:
1163: synchronized (pausedTriggerGroups) {
1164: String[] names = getTriggerGroupNames(ctxt);
1165:
1166: for (int i = 0; i < names.length; i++) {
1167: resumeTriggerGroup(ctxt, names[i]);
1168: }
1169: }
1170: }
1171:
1172: protected boolean applyMisfire(TriggerWrapper tw) {
1173:
1174: long misfireTime = System.currentTimeMillis();
1175: if (getMisfireThreshold() > 0) {
1176: misfireTime -= getMisfireThreshold();
1177: }
1178:
1179: java.util.Date tnft = tw.trigger.getNextFireTime();
1180: if (tnft.getTime() > misfireTime) {
1181: return false;
1182: }
1183:
1184: Calendar cal = null;
1185: if (tw.trigger.getCalendarName() != null) {
1186: cal = retrieveCalendar(null, tw.trigger.getCalendarName());
1187: }
1188:
1189: signaler.notifyTriggerListenersMisfired((Trigger) tw.trigger
1190: .clone());
1191:
1192: tw.trigger.updateAfterMisfire(cal);
1193:
1194: if (tw.trigger.getNextFireTime() == null) {
1195: tw.state = TriggerWrapper.STATE_COMPLETE;
1196: synchronized (triggerLock) {
1197: timeTriggers.remove(tw);
1198: }
1199: } else if (tnft.equals(tw.trigger.getNextFireTime())) {
1200: return false;
1201: }
1202:
1203: return true;
1204: }
1205:
1206: private static long ftrCtr = System.currentTimeMillis();
1207:
1208: protected synchronized String getFiredTriggerRecordId() {
1209: return String.valueOf(ftrCtr++);
1210: }
1211:
1212: /**
1213: * <p>
1214: * Get a handle to the next trigger to be fired, and mark it as 'reserved'
1215: * by the calling scheduler.
1216: * </p>
1217: *
1218: * @see #releaseAcquiredTrigger(SchedulingContext, Trigger)
1219: */
1220: public Trigger acquireNextTrigger(SchedulingContext ctxt,
1221: long noLaterThan) {
1222: TriggerWrapper tw = null;
1223:
1224: synchronized (triggerLock) {
1225:
1226: while (tw == null) {
1227: try {
1228: tw = (TriggerWrapper) timeTriggers.first();
1229: } catch (java.util.NoSuchElementException nsee) {
1230: return null;
1231: }
1232:
1233: if (tw == null) {
1234: return null;
1235: }
1236:
1237: if (tw.trigger.getNextFireTime() == null) {
1238: timeTriggers.remove(tw);
1239: tw = null;
1240: continue;
1241: }
1242:
1243: timeTriggers.remove(tw);
1244:
1245: if (applyMisfire(tw)) {
1246: if (tw.trigger.getNextFireTime() != null) {
1247: timeTriggers.add(tw);
1248: }
1249: tw = null;
1250: continue;
1251: }
1252:
1253: if (tw.trigger.getNextFireTime().getTime() > noLaterThan) {
1254: timeTriggers.add(tw);
1255: return null;
1256: }
1257:
1258: tw.state = TriggerWrapper.STATE_ACQUIRED;
1259:
1260: tw.trigger.setFireInstanceId(getFiredTriggerRecordId());
1261: Trigger trig = (Trigger) tw.trigger.clone();
1262: return trig;
1263: }
1264: }
1265:
1266: return null;
1267: }
1268:
1269: /**
1270: * <p>
1271: * Inform the <code>JobStore</code> that the scheduler no longer plans to
1272: * fire the given <code>Trigger</code>, that it had previously acquired
1273: * (reserved).
1274: * </p>
1275: */
1276: public void releaseAcquiredTrigger(SchedulingContext ctxt,
1277: Trigger trigger) {
1278: synchronized (triggerLock) {
1279: TriggerWrapper tw = (TriggerWrapper) triggersByFQN
1280: .get(TriggerWrapper.getTriggerNameKey(trigger));
1281: if (tw != null && tw.state == TriggerWrapper.STATE_ACQUIRED) {
1282: tw.state = TriggerWrapper.STATE_WAITING;
1283: timeTriggers.add(tw);
1284: }
1285: }
1286: }
1287:
1288: /**
1289: * <p>
1290: * Inform the <code>JobStore</code> that the scheduler is now firing the
1291: * given <code>Trigger</code> (executing its associated <code>Job</code>),
1292: * that it had previously acquired (reserved).
1293: * </p>
1294: */
1295: public TriggerFiredBundle triggerFired(SchedulingContext ctxt,
1296: Trigger trigger) {
1297:
1298: synchronized (triggerLock) {
1299: TriggerWrapper tw = (TriggerWrapper) triggersByFQN
1300: .get(TriggerWrapper.getTriggerNameKey(trigger));
1301: // was the trigger deleted since being acquired?
1302: if (tw == null || tw.trigger == null) {
1303: return null;
1304: }
1305: // was the trigger completed since being acquired?
1306: if (tw.state == TriggerWrapper.STATE_COMPLETE) {
1307: return null;
1308: }
1309: // was the trigger paused since being acquired?
1310: if (tw.state == TriggerWrapper.STATE_PAUSED) {
1311: return null;
1312: }
1313: // was the trigger blocked since being acquired?
1314: if (tw.state == TriggerWrapper.STATE_BLOCKED) {
1315: return null;
1316: }
1317: // was the trigger paused and blocked since being acquired?
1318: if (tw.state == TriggerWrapper.STATE_PAUSED_BLOCKED) {
1319: return null;
1320: }
1321:
1322: Calendar cal = null;
1323: if (tw.trigger.getCalendarName() != null) {
1324: cal = retrieveCalendar(ctxt, tw.trigger
1325: .getCalendarName());
1326: }
1327: Date prevFireTime = trigger.getPreviousFireTime();
1328: // call triggered on our copy, and the scheduler's copy
1329: tw.trigger.triggered(cal);
1330: trigger.triggered(cal);
1331: //tw.state = TriggerWrapper.STATE_EXECUTING;
1332: tw.state = TriggerWrapper.STATE_WAITING;
1333:
1334: TriggerFiredBundle bndle = new TriggerFiredBundle(
1335: retrieveJob(ctxt, trigger.getJobName(), trigger
1336: .getJobGroup()), trigger, cal, false,
1337: new Date(), trigger.getPreviousFireTime(),
1338: prevFireTime, trigger.getNextFireTime());
1339:
1340: JobDetail job = bndle.getJobDetail();
1341:
1342: if (job.isStateful()) {
1343: ArrayList trigs = getTriggerWrappersForJob(job
1344: .getName(), job.getGroup());
1345: Iterator itr = trigs.iterator();
1346: while (itr.hasNext()) {
1347: TriggerWrapper ttw = (TriggerWrapper) itr.next();
1348: if (ttw.state == TriggerWrapper.STATE_WAITING) {
1349: ttw.state = TriggerWrapper.STATE_BLOCKED;
1350: }
1351: if (ttw.state == TriggerWrapper.STATE_PAUSED) {
1352: ttw.state = TriggerWrapper.STATE_PAUSED_BLOCKED;
1353: }
1354: timeTriggers.remove(ttw);
1355: }
1356: blockedJobs.add(JobWrapper.getJobNameKey(job));
1357: } else if (tw.trigger.getNextFireTime() != null) {
1358: synchronized (triggerLock) {
1359: timeTriggers.add(tw);
1360: }
1361: }
1362:
1363: return bndle;
1364: }
1365: }
1366:
1367: /**
1368: * <p>
1369: * Inform the <code>JobStore</code> that the scheduler has completed the
1370: * firing of the given <code>Trigger</code> (and the execution its
1371: * associated <code>Job</code>), and that the <code>{@link org.quartz.JobDataMap}</code>
1372: * in the given <code>JobDetail</code> should be updated if the <code>Job</code>
1373: * is stateful.
1374: * </p>
1375: */
1376: public void triggeredJobComplete(SchedulingContext ctxt,
1377: Trigger trigger, JobDetail jobDetail, int triggerInstCode) {
1378:
1379: synchronized (triggerLock) {
1380:
1381: String jobKey = JobWrapper.getJobNameKey(jobDetail
1382: .getName(), jobDetail.getGroup());
1383: JobWrapper jw = (JobWrapper) jobsByFQN.get(jobKey);
1384: TriggerWrapper tw = (TriggerWrapper) triggersByFQN
1385: .get(TriggerWrapper.getTriggerNameKey(trigger));
1386:
1387: // It's possible that the job is null if:
1388: // 1- it was deleted during execution
1389: // 2- RAMJobStore is being used only for volatile jobs / triggers
1390: // from the JDBC job store
1391: if (jw != null) {
1392: JobDetail jd = jw.jobDetail;
1393:
1394: if (jd.isStateful()) {
1395: JobDataMap newData = jobDetail.getJobDataMap();
1396: if (newData != null) {
1397: newData = (JobDataMap) newData.clone();
1398: newData.clearDirtyFlag();
1399: }
1400: jd.setJobDataMap(newData);
1401: blockedJobs.remove(JobWrapper.getJobNameKey(jd));
1402: ArrayList trigs = getTriggerWrappersForJob(jd
1403: .getName(), jd.getGroup());
1404: Iterator itr = trigs.iterator();
1405: while (itr.hasNext()) {
1406: TriggerWrapper ttw = (TriggerWrapper) itr
1407: .next();
1408: if (ttw.state == TriggerWrapper.STATE_BLOCKED) {
1409: ttw.state = TriggerWrapper.STATE_WAITING;
1410: timeTriggers.add(ttw);
1411: }
1412: if (ttw.state == TriggerWrapper.STATE_PAUSED_BLOCKED) {
1413: ttw.state = TriggerWrapper.STATE_PAUSED;
1414: }
1415: }
1416: }
1417: } else { // even if it was deleted, there may be cleanup to do
1418: blockedJobs.remove(JobWrapper.getJobNameKey(jobDetail));
1419: }
1420:
1421: // check for trigger deleted during execution...
1422: if (tw != null) {
1423: if (triggerInstCode == Trigger.INSTRUCTION_DELETE_TRIGGER) {
1424:
1425: if (trigger.getNextFireTime() == null) {
1426: // double check for possible reschedule within job
1427: // execution, which would cancel the need to delete...
1428: if (tw.getTrigger().getNextFireTime() == null) {
1429: removeTrigger(ctxt, trigger.getName(),
1430: trigger.getGroup());
1431: }
1432: } else {
1433: removeTrigger(ctxt, trigger.getName(), trigger
1434: .getGroup());
1435: }
1436: } else if (triggerInstCode == Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE) {
1437: tw.state = TriggerWrapper.STATE_COMPLETE;
1438: timeTriggers.remove(tw);
1439: } else if (triggerInstCode == Trigger.INSTRUCTION_SET_TRIGGER_ERROR) {
1440: getLog().info(
1441: "Trigger " + trigger.getFullName()
1442: + " set to ERROR state.");
1443: tw.state = TriggerWrapper.STATE_ERROR;
1444: } else if (triggerInstCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR) {
1445: getLog().info(
1446: "All triggers of Job "
1447: + trigger.getFullJobName()
1448: + " set to ERROR state.");
1449: setAllTriggersOfJobToState(trigger.getJobName(),
1450: trigger.getJobGroup(),
1451: TriggerWrapper.STATE_ERROR);
1452: } else if (triggerInstCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE) {
1453: setAllTriggersOfJobToState(trigger.getJobName(),
1454: trigger.getJobGroup(),
1455: TriggerWrapper.STATE_COMPLETE);
1456: }
1457: }
1458: }
1459: }
1460:
1461: protected void setAllTriggersOfJobToState(String jobName,
1462: String jobGroup, int state) {
1463: ArrayList tws = getTriggerWrappersForJob(jobName, jobGroup);
1464: Iterator itr = tws.iterator();
1465: while (itr.hasNext()) {
1466: TriggerWrapper tw = (TriggerWrapper) itr.next();
1467: tw.state = state;
1468: if (state != TriggerWrapper.STATE_WAITING) {
1469: timeTriggers.remove(tw);
1470: }
1471: }
1472: }
1473:
1474: protected String peekTriggers() {
1475:
1476: StringBuffer str = new StringBuffer();
1477: TriggerWrapper tw = null;
1478: synchronized (triggerLock) {
1479: for (Iterator valueIter = triggersByFQN.values().iterator(); valueIter
1480: .hasNext();) {
1481: tw = (TriggerWrapper) valueIter.next();
1482: str.append(tw.trigger.getName());
1483: str.append("/");
1484: }
1485: }
1486: str.append(" | ");
1487:
1488: synchronized (triggerLock) {
1489: Iterator itr = timeTriggers.iterator();
1490: while (itr.hasNext()) {
1491: tw = (TriggerWrapper) itr.next();
1492: str.append(tw.trigger.getName());
1493: str.append("->");
1494: }
1495: }
1496:
1497: return str.toString();
1498: }
1499:
1500: /**
1501: * @see org.quartz.spi.JobStore#getPausedTriggerGroups(org.quartz.core.SchedulingContext)
1502: */
1503: public Set getPausedTriggerGroups(SchedulingContext ctxt)
1504: throws JobPersistenceException {
1505: HashSet set = new HashSet();
1506:
1507: set.addAll(pausedTriggerGroups);
1508:
1509: return set;
1510: }
1511:
1512: }
1513:
1514: /*******************************************************************************
1515: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1516: *
1517: * Helper Classes. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1518: */
1519:
1520: class TriggerComparator implements Comparator {
1521:
1522: public int compare(Object obj1, Object obj2) {
1523: TriggerWrapper trig1 = (TriggerWrapper) obj1;
1524: TriggerWrapper trig2 = (TriggerWrapper) obj2;
1525:
1526: int comp = trig1.trigger.compareTo(trig2.trigger);
1527: if (comp != 0) {
1528: return comp;
1529: }
1530:
1531: comp = trig2.trigger.getPriority()
1532: - trig1.trigger.getPriority();
1533: if (comp != 0) {
1534: return comp;
1535: }
1536:
1537: return trig1.trigger.getFullName().compareTo(
1538: trig2.trigger.getFullName());
1539: }
1540:
1541: public boolean equals(Object obj) {
1542: return (obj instanceof TriggerComparator);
1543: }
1544: }
1545:
1546: class JobWrapper {
1547:
1548: public String key;
1549:
1550: public JobDetail jobDetail;
1551:
1552: JobWrapper(JobDetail jobDetail) {
1553: this .jobDetail = jobDetail;
1554: key = getJobNameKey(jobDetail);
1555: }
1556:
1557: JobWrapper(JobDetail jobDetail, String key) {
1558: this .jobDetail = jobDetail;
1559: this .key = key;
1560: }
1561:
1562: static String getJobNameKey(JobDetail jobDetail) {
1563: return jobDetail.getGroup() + "_$x$x$_" + jobDetail.getName();
1564: }
1565:
1566: static String getJobNameKey(String jobName, String groupName) {
1567: return groupName + "_$x$x$_" + jobName;
1568: }
1569:
1570: public boolean equals(Object obj) {
1571: if (obj instanceof JobWrapper) {
1572: JobWrapper jw = (JobWrapper) obj;
1573: if (jw.key.equals(this .key)) {
1574: return true;
1575: }
1576: }
1577:
1578: return false;
1579: }
1580:
1581: public int hashCode() {
1582: return key.hashCode();
1583: }
1584:
1585: }
1586:
1587: class TriggerWrapper {
1588:
1589: public String key;
1590:
1591: public String jobKey;
1592:
1593: public Trigger trigger;
1594:
1595: public int state = STATE_WAITING;
1596:
1597: public static final int STATE_WAITING = 0;
1598:
1599: public static final int STATE_ACQUIRED = 1;
1600:
1601: public static final int STATE_EXECUTING = 2;
1602:
1603: public static final int STATE_COMPLETE = 3;
1604:
1605: public static final int STATE_PAUSED = 4;
1606:
1607: public static final int STATE_BLOCKED = 5;
1608:
1609: public static final int STATE_PAUSED_BLOCKED = 6;
1610:
1611: public static final int STATE_ERROR = 7;
1612:
1613: TriggerWrapper(Trigger trigger) {
1614: this .trigger = trigger;
1615: key = getTriggerNameKey(trigger);
1616: this .jobKey = JobWrapper.getJobNameKey(trigger.getJobName(),
1617: trigger.getJobGroup());
1618: }
1619:
1620: static String getTriggerNameKey(Trigger trigger) {
1621: return trigger.getGroup() + "_$x$x$_" + trigger.getName();
1622: }
1623:
1624: static String getTriggerNameKey(String triggerName, String groupName) {
1625: return groupName + "_$x$x$_" + triggerName;
1626: }
1627:
1628: public boolean equals(Object obj) {
1629: if (obj instanceof TriggerWrapper) {
1630: TriggerWrapper tw = (TriggerWrapper) obj;
1631: if (tw.key.equals(this .key)) {
1632: return true;
1633: }
1634: }
1635:
1636: return false;
1637: }
1638:
1639: public int hashCode() {
1640: return key.hashCode();
1641: }
1642:
1643: public Trigger getTrigger() {
1644: return this.trigger;
1645: }
1646: }
|