/** * Copyright 2005 The Apache Software Foundation * * Licensed under the 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.apache.nutch.admin.scheduling; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.Calendar; import org.quartz.JobDataMap; 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.JobStore; import org.quartz.spi.SchedulerSignaler; import org.quartz.spi.TriggerFiredBundle; /** * * This class implements a <code>{@link org.quartz.spi.JobStore}</code> that * utilizes the FileSystem as its storage device. */ public class FileJobStore implements JobStore { private static Log log = LogFactory.getLog(FileJobStore.class); // persistent fields private HashMap fJobsByName; private HashMap fTriggersByName = new HashMap(); private HashMap fCalendarsByName = new HashMap(); private HashMap fTriggerStatesByName = new HashMap(); private HashSet fPausedTriggerGroups = new HashSet(); // transient fields private SchedulerSignaler fSignaler; private long fMisfireThreshold = 5001; private FileJobStoreSerializer fSerializer; private String fStoreDirectory = "quartzstore"; private HashMap fTriggersByGroup = new HashMap(); private HashMap fJobsByGroup = new HashMap(); private TreeSet fOrderedTriggers = new TreeSet(); private HashSet fBlockedJobs = new HashSet(); public void initialize(ClassLoadHelper loadHelper, SchedulerSignaler signaler) throws SchedulerConfigException { this.fSignaler = signaler; try { this.fSerializer = new FileJobStoreSerializer(this.fStoreDirectory); initPeristentFields(); } catch (JobPersistenceException e) { throw new SchedulerConfigException("could not load persistent data ", e); } fillTransientFields(); log.info("FileJobStore initialized : " + this.fSerializer.getStoreDirectory().getAbsolutePath()); } public void schedulerStarted() throws SchedulerException { // nothing to do } public void shutdown() { // nothing to do } public boolean supportsPersistence() { return true; } public void storeJobAndTrigger( SchedulingContext ctxt, JobDetail newJob, Trigger newTrigger) throws ObjectAlreadyExistsException, JobPersistenceException { storeJob(ctxt, newJob, false); storeTrigger(ctxt, newTrigger, false); } public void storeJob( SchedulingContext ctxt, JobDetail newJobDetail, boolean replaceExisting) throws ObjectAlreadyExistsException, JobPersistenceException { boolean jobExists = this.fJobsByName.containsKey(newJobDetail.getFullName()); if (!replaceExisting && jobExists) { throw new ObjectAlreadyExistsException(newJobDetail); } else if (replaceExisting && jobExists) { log.warn("overwriting existing job: " + newJobDetail.getFullName()); } synchronized (this.fJobsByName) { if (!jobExists) { addJobGroup(newJobDetail); } this.fJobsByName.put(newJobDetail.getFullName(), newJobDetail); this.fSerializer.saveJobs(this.fJobsByName); } } private void addJobGroup(JobDetail newJob) { HashMap groupMap = (HashMap) this.fJobsByGroup.get(newJob.getGroup()); if (groupMap == null) { groupMap = new HashMap(); this.fJobsByGroup.put(newJob.getGroup(), groupMap); } groupMap.put(newJob.getName(), newJob); } /** * * @param group * @param name * @return the full name of a job or a trigger (i.e. * 'getFullName(trigger.getGroup(),trigger.getName())' should equal * 'trigger.getFullName()') */ private static String getFullName(String group, String name) { StringBuffer buffer = new StringBuffer(group); buffer.append("."); buffer.append(name); return buffer.toString(); } public boolean removeJob( SchedulingContext ctxt, String jobName, String groupName) throws JobPersistenceException { JobDetail job = (JobDetail) this.fJobsByName.get(getFullName(groupName, jobName)); if (job == null) { return false; } Trigger[] triggers = getTriggersForJob(ctxt, jobName, groupName); for (int i = 0; i < triggers.length; i++) { removeTrigger(ctxt, triggers[i].getName(), triggers[i].getGroup()); } synchronized (this.fJobsByName) { HashMap groupMap = (HashMap) this.fJobsByGroup.get(groupName); if (groupMap != null) { groupMap.remove(jobName); if (groupMap.size() == 0) { this.fJobsByGroup.remove(groupName); } } this.fJobsByName.remove(job.getFullName()); this.fBlockedJobs.remove(job); this.fSerializer.saveJobs(this.fJobsByName); } return true; } public JobDetail retrieveJob( SchedulingContext ctxt, String jobName, String groupName) throws JobPersistenceException { return (JobDetail) this.fJobsByName.get(getFullName(groupName, jobName)); } public void storeTrigger( SchedulingContext ctxt, Trigger newTrigger, boolean replaceExisting) throws ObjectAlreadyExistsException, JobPersistenceException { if (this.fTriggersByName.containsKey(newTrigger.getFullName())) { if (!replaceExisting) { throw new ObjectAlreadyExistsException(newTrigger); } else { log.warn("overwriting existing trigger: " + newTrigger.getFullName()); } removeTrigger(ctxt, newTrigger.getName(), newTrigger.getGroup()); } if (!this.fJobsByName.containsKey(newTrigger.getFullJobName())) { throw new JobPersistenceException("The job (" + newTrigger.getFullJobName() + ") referenced by the trigger does not exist."); } synchronized (this.fTriggersByName) { addTriggerGroup(newTrigger); this.fTriggersByName.put(newTrigger.getFullName(), newTrigger); this.fSerializer.saveTriggers(this.fTriggersByName); synchronized (this.fPausedTriggerGroups) { if (this.fPausedTriggerGroups.contains(newTrigger.getGroup())) { setTriggerState(ctxt, newTrigger, Trigger.STATE_PAUSED); } else { this.fOrderedTriggers.add(newTrigger); } } } } private void addTriggerGroup(Trigger newTrigger) { HashMap groupMap = (HashMap) this.fTriggersByGroup.get(newTrigger.getGroup()); if (groupMap == null) { groupMap = new HashMap(); this.fTriggersByGroup.put(newTrigger.getGroup(), groupMap); } groupMap.put(newTrigger.getName(), newTrigger); } public boolean removeTrigger( SchedulingContext ctxt, String triggerName, String groupName) throws JobPersistenceException { synchronized (this.fTriggersByName) { // remove from triggers map Trigger trigger = (Trigger) this.fTriggersByName.remove(getFullName( groupName, triggerName)); if (trigger == null) { return false; } this.fSerializer.saveTriggers(this.fTriggersByName); // remove from triggers by group HashMap grpMap = (HashMap) this.fTriggersByGroup.get(groupName); if (grpMap != null) { grpMap.remove(triggerName); if (grpMap.size() == 0) this.fTriggersByGroup.remove(groupName); } this.fOrderedTriggers.remove(trigger); JobDetail jobDetail = retrieveJob(ctxt, trigger.getJobName(), trigger.getJobGroup()); // remove state setTriggerState(ctxt, trigger, Trigger.STATE_NONE); if (!jobDetail.isDurable() && getTriggersForJob(ctxt, jobDetail.getName(), jobDetail.getGroup()).length == 0) { removeJob(ctxt, jobDetail.getName(), jobDetail.getGroup()); } } return true; } public boolean replaceTrigger( SchedulingContext ctxt, String triggerName, String groupName, Trigger newTrigger) throws JobPersistenceException { Trigger oldTrigger = retrieveTrigger(ctxt, triggerName, groupName); if (oldTrigger == null) return false; if (!oldTrigger.getJobName().equals(newTrigger.getJobName()) || !oldTrigger.getJobGroup().equals(newTrigger.getJobGroup())) throw new JobPersistenceException( "New trigger is not related to the same job as the old trigger."); JobDetail jobDetail = retrieveJob(ctxt, oldTrigger.getJobName(), oldTrigger.getJobGroup()); synchronized (this.fTriggersByName) { removeTrigger(ctxt, triggerName, groupName); if (!this.fJobsByName.containsKey(oldTrigger.getFullJobName())) { storeJob(ctxt, jobDetail, false); } storeTrigger(ctxt, newTrigger, false); } return true; } public Trigger retrieveTrigger( SchedulingContext ctxt, String triggerName, String groupName) throws JobPersistenceException { return (Trigger) this.fTriggersByName.get(getFullName( groupName, triggerName)); } public void storeCalendar( SchedulingContext ctxt, String name, Calendar calendar, boolean replaceExisting, boolean updateTriggers) throws ObjectAlreadyExistsException, JobPersistenceException { boolean calenderExists = this.fCalendarsByName.containsKey(name); if (calenderExists) { if (!replaceExisting) { throw new ObjectAlreadyExistsException("Calendar with name '" + name + "' already exists."); } this.fCalendarsByName.remove(name); } this.fCalendarsByName.put(name, calendar); this.fSerializer.saveCalendars(this.fCalendarsByName); // update triggers if (calenderExists && updateTriggers) { synchronized (this.fTriggersByName) { Trigger[] triggers = getTriggerForCalendar(name); for (int i = 0; i < triggers.length; i++) { boolean removed = this.fOrderedTriggers.remove(triggers[i]); triggers[i].updateWithNewCalendar(calendar, getMisfireThreshold()); if (removed) { this.fOrderedTriggers.add(triggers[i]); } } } } } public boolean removeCalendar(SchedulingContext ctxt, String calName) throws JobPersistenceException { if (getTriggerForCalendar(calName).length > 0) { throw new JobPersistenceException( "Calender cannot be removed if it referenced by a Trigger!"); } boolean exists = this.fCalendarsByName.remove(calName) != null; if (exists) { this.fSerializer.saveCalendars(this.fCalendarsByName); } return exists; } public Calendar retrieveCalendar(SchedulingContext ctxt, String calName) throws JobPersistenceException { return (Calendar) this.fCalendarsByName.get(calName); } public int getNumberOfJobs(SchedulingContext ctxt) throws JobPersistenceException { return this.fJobsByName.size(); } public int getNumberOfTriggers(SchedulingContext ctxt) throws JobPersistenceException { return this.fTriggersByName.size(); } public int getNumberOfCalendars(SchedulingContext ctxt) throws JobPersistenceException { return this.fCalendarsByName.size(); } public String[] getJobNames(SchedulingContext ctxt, String groupName) throws JobPersistenceException { HashMap groupMap = (HashMap) this.fJobsByGroup.get(groupName); if (groupMap == null) return new String[0]; return (String[]) groupMap.keySet().toArray( new String[groupMap.keySet().size()]); } public String[] getTriggerNames(SchedulingContext ctxt, String groupName) throws JobPersistenceException { HashMap groupMap = (HashMap) this.fTriggersByGroup.get(groupName); if (groupMap == null) return new String[0]; return (String[]) groupMap.keySet().toArray( new String[groupMap.keySet().size()]); } public String[] getJobGroupNames(SchedulingContext ctxt) throws JobPersistenceException { return (String[]) this.fJobsByGroup.keySet().toArray( new String[this.fJobsByGroup.keySet().size()]); } public String[] getTriggerGroupNames(SchedulingContext ctxt) throws JobPersistenceException { return (String[]) this.fTriggersByGroup.keySet().toArray( new String[this.fTriggersByGroup.keySet().size()]); } public String[] getCalendarNames(SchedulingContext ctxt) throws JobPersistenceException { return (String[]) this.fCalendarsByName.keySet().toArray( new String[this.fCalendarsByName.keySet().size()]); } public Trigger[] getTriggersForJob( SchedulingContext ctxt, String jobName, String groupName) throws JobPersistenceException { String jobFullName = getFullName(groupName, jobName); ArrayList triggers = new ArrayList(); synchronized (this.fTriggersByName) { for (Iterator iter = this.fTriggersByName.values().iterator(); iter .hasNext();) { Trigger trigger = (Trigger) iter.next(); if (trigger.getFullJobName().equals(jobFullName)) triggers.add(trigger); } } return (Trigger[]) triggers.toArray(new Trigger[triggers.size()]); } private Trigger[] getTriggerForCalendar(String calName) { ArrayList triggers = new ArrayList(); synchronized (this.fTriggersByName) { for (Iterator iter = this.fTriggersByName.values().iterator(); iter .hasNext();) { Trigger trigger = (Trigger) iter.next(); if (calName.equals(trigger.getCalendarName())) { triggers.add(trigger); } } } return (Trigger[]) triggers.toArray(new Trigger[triggers.size()]); } /* * trigger_states and their intern implementation * * STATE_NONE - not in TriggersByName ................................. * STATE_NORMAL - not in TriggerStatesByName .......................... * STATE_COMPLETE - with int value in TriggerStatesByName ............. * STATE_ERROR - with int value in TriggerStatesByName ................ * STATE_PAUSED - with int value in TriggerStatesByName ............... * STATE_BLOCKED - has job in BlockedJobs ............................. */ public int getTriggerState( SchedulingContext ctxt, String triggerName, String triggerGroup) throws JobPersistenceException { Trigger trigger = retrieveTrigger(ctxt, triggerName, triggerGroup); if (trigger == null) return Trigger.STATE_NONE; Integer triggerState = (Integer) this.fTriggerStatesByName.get(trigger.getFullName()); boolean jobBlocked = this.fBlockedJobs.contains(trigger.getFullJobName()); if (jobBlocked) { if (triggerState == null || triggerState.intValue() != Trigger.STATE_PAUSED) return Trigger.STATE_BLOCKED; } if (triggerState == null) return Trigger.STATE_NORMAL; return triggerState.intValue(); } private void setTriggerState( SchedulingContext ctxt, Trigger trigger, int state) throws JobPersistenceException { int oldState = getTriggerState(null, trigger.getName(), trigger.getGroup()); if (oldState == Trigger.STATE_COMPLETE && oldState != Trigger.STATE_NONE) return; switch (state) { case Trigger.STATE_NONE: this.fTriggerStatesByName.remove(trigger.getFullName()); this.fSerializer.saveTriggerStates(this.fTriggerStatesByName); break; case Trigger.STATE_NORMAL: this.fTriggerStatesByName.remove(trigger.getFullName()); this.fSerializer.saveTriggerStates(this.fTriggerStatesByName); break; case Trigger.STATE_BLOCKED: // do nothing cause the state will be identified by blocked jobs (so // we'll not overwrite paused-,error-states) break; default: // otherwise(pause,error,complete) just write the state this.fTriggerStatesByName.put(trigger.getFullName(), new Integer(state)); this.fSerializer.saveTriggerStates(this.fTriggerStatesByName); break; } } public void pauseTrigger( SchedulingContext ctxt, String triggerName, String groupName) throws JobPersistenceException { int triggerState = getTriggerState(ctxt, triggerName, groupName); if (triggerState != Trigger.STATE_NORMAL && triggerState != Trigger.STATE_ERROR) return; Trigger trigger = retrieveTrigger(ctxt, triggerName, groupName); synchronized (this.fTriggersByName) { setTriggerState(ctxt, trigger, Trigger.STATE_PAUSED); this.fOrderedTriggers.remove(this.fTriggersByName.get(trigger .getFullName())); } } public void pauseTriggerGroup(SchedulingContext ctxt, String groupName) throws JobPersistenceException { synchronized (this.fPausedTriggerGroups) { if (this.fPausedTriggerGroups.contains(groupName)) return; this.fPausedTriggerGroups.add(groupName); this.fSerializer.savePausedTriggerGroups(this.fPausedTriggerGroups); String[] names = getTriggerNames(ctxt, groupName); for (int i = 0; i < names.length; i++) { pauseTrigger(ctxt, names[i], groupName); } } } public void pauseJob(SchedulingContext ctxt, String jobName, String groupName) throws JobPersistenceException { synchronized (this.fPausedTriggerGroups) { Trigger[] triggers = getTriggersForJob(ctxt, jobName, groupName); for (int j = 0; j < triggers.length; j++) { pauseTrigger(ctxt, triggers[j].getName(), triggers[j].getGroup()); } } } public void pauseJobGroup(SchedulingContext ctxt, String groupName) throws JobPersistenceException { synchronized (this.fPausedTriggerGroups) { String[] jobNames = getJobNames(ctxt, groupName); for (int i = 0; i < jobNames.length; i++) { Trigger[] triggers = getTriggersForJob(ctxt, jobNames[i], groupName); for (int j = 0; j < triggers.length; j++) { pauseTrigger(ctxt, triggers[j].getName(), triggers[j].getGroup()); } } } } public void resumeTrigger( SchedulingContext ctxt, String triggerName, String groupName) throws JobPersistenceException { int triggerState = getTriggerState(ctxt, triggerName, groupName); if (triggerState != Trigger.STATE_PAUSED) return; Trigger trigger = retrieveTrigger(ctxt, triggerName, groupName); synchronized (this.fTriggersByName) { if (this.fBlockedJobs.contains(trigger.getFullJobName())) { setTriggerState(ctxt, trigger, Trigger.STATE_BLOCKED); } else { setTriggerState(ctxt, trigger, Trigger.STATE_NORMAL); } applyMisfire(ctxt, trigger); if (getTriggerState(null, triggerName, groupName) == Trigger.STATE_NORMAL) { this.fOrderedTriggers.add(trigger); } } } private boolean applyMisfire(SchedulingContext ctxt, Trigger trigger) throws JobPersistenceException { long misfireTime = System.currentTimeMillis() - getMisfireThreshold(); Date nextFireTime = trigger.getNextFireTime(); if (nextFireTime.getTime() > misfireTime) { return false; // we are in time } // notify for misfire this.fSignaler.notifyTriggerListenersMisfired(trigger); // calculate next fire time Calendar calender = retrieveCalendar(null, trigger.getCalendarName()); trigger.updateAfterMisfire(calender); if (trigger.getNextFireTime() == null) { setTriggerState(ctxt, trigger, Trigger.STATE_COMPLETE); synchronized (this.fTriggersByName) { this.fOrderedTriggers.remove(trigger); } } else if (nextFireTime.equals(trigger.getNextFireTime())) return false; return true; } public void resumeTriggerGroup(SchedulingContext ctxt, String groupName) throws JobPersistenceException { synchronized (this.fPausedTriggerGroups) { String[] names = getTriggerNames(ctxt, groupName); for (int i = 0; i < names.length; i++) { resumeTrigger(ctxt, names[i], groupName); } this.fPausedTriggerGroups.remove(groupName); this.fSerializer.saveTriggerStates(this.fTriggerStatesByName); } } public Set getPausedTriggerGroups(SchedulingContext ctxt) throws JobPersistenceException { HashSet set = new HashSet(); set.addAll(this.fPausedTriggerGroups); return set; } public void resumeJob(SchedulingContext ctxt, String jobName, String groupName) throws JobPersistenceException { synchronized (this.fPausedTriggerGroups) { Trigger[] triggers = getTriggersForJob(ctxt, jobName, groupName); for (int j = 0; j < triggers.length; j++) { resumeTrigger(ctxt, triggers[j].getName(), triggers[j].getGroup()); } } } public void resumeJobGroup(SchedulingContext ctxt, String groupName) throws JobPersistenceException { synchronized (this.fPausedTriggerGroups) { String[] jobNames = getJobNames(ctxt, groupName); for (int i = 0; i < jobNames.length; i++) { Trigger[] triggers = getTriggersForJob(ctxt, jobNames[i], groupName); for (int j = 0; j < triggers.length; j++) { resumeTrigger(ctxt, triggers[j].getName(), triggers[j].getGroup()); } } } } public void pauseAll(SchedulingContext ctxt) throws JobPersistenceException { synchronized (this.fPausedTriggerGroups) { String[] names = getTriggerGroupNames(ctxt); for (int i = 0; i < names.length; i++) { pauseTriggerGroup(ctxt, names[i]); } } } public void resumeAll(SchedulingContext ctxt) throws JobPersistenceException { synchronized (this.fPausedTriggerGroups) { String[] names = getTriggerGroupNames(ctxt); for (int i = 0; i < names.length; i++) { resumeTriggerGroup(ctxt, names[i]); } } } public Trigger acquireNextTrigger(SchedulingContext ctxt, long noLaterThan) throws JobPersistenceException { synchronized (this.fTriggersByName) { while (!this.fOrderedTriggers.isEmpty()) { Trigger trigger = (Trigger) this.fOrderedTriggers.first(); this.fOrderedTriggers.remove(trigger); // have one canditate if (trigger.getNextFireTime() != null && !applyMisfire(ctxt, trigger)) { if (trigger.getNextFireTime().getTime() > noLaterThan) { this.fOrderedTriggers.add(trigger); return null; } trigger.setFireInstanceId(getFiredTriggerRecordId()); this.fSerializer.saveTriggers(this.fTriggersByName); return (Trigger) trigger.clone(); } if (trigger.getNextFireTime() != null) { this.fOrderedTriggers.add(trigger); } } } return null; } public void releaseAcquiredTrigger(SchedulingContext ctxt, Trigger trigger) throws JobPersistenceException { trigger = (Trigger) this.fTriggersByName.get(trigger.getFullName()); if (trigger != null) { this.fOrderedTriggers.add(trigger); } } public TriggerFiredBundle triggerFired(SchedulingContext ctxt, Trigger trigger) throws JobPersistenceException { synchronized (this.fTriggersByName) { Trigger storeTrigger = (Trigger) this.fTriggersByName.get(trigger.getFullName()); int triggerState = getTriggerState(ctxt, trigger.getName(), trigger.getGroup()); if (triggerState != Trigger.STATE_NORMAL) return null; Calendar calender = retrieveCalendar(ctxt, storeTrigger.getCalendarName()); Date prevFireTime = trigger.getPreviousFireTime(); // call triggered on our copy, and the scheduler's copy trigger.triggered(calender); storeTrigger.triggered(calender); TriggerFiredBundle fireBundle = new TriggerFiredBundle( retrieveJob(ctxt, trigger.getJobName(), trigger.getJobGroup()), trigger, calender, false, new Date(), trigger .getPreviousFireTime(), prevFireTime, trigger .getNextFireTime()); JobDetail job = fireBundle.getJobDetail(); if (job.isStateful()) { Trigger[] triggers = getTriggersForJob(ctxt, job.getName(), job.getGroup()); for (int i = 0; i < triggers.length; i++) { setTriggerState(ctxt, triggers[i], Trigger.STATE_BLOCKED); this.fOrderedTriggers.remove(triggers[i]); } this.fBlockedJobs.add(job.getFullName()); } else if (storeTrigger.getNextFireTime() != null) { this.fOrderedTriggers.add(storeTrigger); } return fireBundle; } } public void triggeredJobComplete( SchedulingContext ctxt, Trigger trigger, JobDetail jobDetail, int triggerInstCode) throws JobPersistenceException { synchronized (this.fTriggersByName) { this.fBlockedJobs.remove(jobDetail.getFullName()); JobDetail oldJobDetail = retrieveJob(ctxt, jobDetail.getName(), jobDetail.getGroup()); Trigger oldTrigger = (Trigger) this.fTriggersByName.get(trigger.getFullName()); updateJobDataMap(jobDetail, oldJobDetail); updateTriggerState(ctxt, trigger, oldTrigger, triggerInstCode); } } private void updateJobDataMap(JobDetail newJob, JobDetail oldJob) throws JobPersistenceException { if (oldJob == null || !newJob.isStateful()) return; JobDataMap newData = newJob.getJobDataMap(); if (newData != null) newData.clearDirtyFlag(); oldJob.setJobDataMap(newData); this.fSerializer.saveJobs(this.fJobsByName); } private void updateTriggerState( SchedulingContext ctxt, Trigger newTrigger, Trigger oldTrigger, int triggerInstruction) throws JobPersistenceException { if (oldTrigger == null) return; switch (triggerInstruction) { case Trigger.INSTRUCTION_DELETE_TRIGGER: if (newTrigger.getNextFireTime() != null) removeTrigger(ctxt, newTrigger.getName(), newTrigger.getGroup()); else if (oldTrigger.getNextFireTime() == null) removeTrigger(ctxt, newTrigger.getName(), newTrigger.getGroup()); break; case Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE: setTriggerState(ctxt, oldTrigger, Trigger.STATE_COMPLETE); this.fOrderedTriggers.remove(oldTrigger); break; case Trigger.INSTRUCTION_SET_TRIGGER_ERROR: setTriggerState(ctxt, oldTrigger, Trigger.STATE_ERROR); log.info("Trigger " + oldTrigger.getFullName() + " set to ERROR state."); break; case Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE: Trigger[] triggers1 = getTriggersForJob(ctxt, oldTrigger.getJobName(), oldTrigger .getJobGroup()); for (int i = 0; i < triggers1.length; i++) { setTriggerState(ctxt, triggers1[i], Trigger.STATE_COMPLETE); } break; case Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR: Trigger[] triggers2 = getTriggersForJob(ctxt, oldTrigger.getJobName(), oldTrigger .getJobGroup()); for (int i = 0; i < triggers2.length; i++) { setTriggerState(ctxt, triggers2[i], Trigger.STATE_ERROR); } log.info("All triggers of Job " + oldTrigger.getFullJobName() + " set to ERROR state."); break; } } // -------------------------non-interface--------------------- /** * @return the number of milliseconds in those the job would be executed * delayed before misfired. */ public long getMisfireThreshold() { return this.fMisfireThreshold; } /** * @param misfireThreshold * the number of milliseconds in those the job would be executed * delayed before misfired */ public void setMisfireThreshold(long misfireThreshold) { this.fMisfireThreshold = misfireThreshold; } /** * @return Returns the storeFilePath. */ public String getStoreFilePath() { return this.fStoreDirectory; } /** * @param storeFilePath * The storeFilePath to set. */ public void setStoreFilePath(String storeFilePath) { this.fStoreDirectory = storeFilePath; } /** * Removes all persitent data. */ public void clear() { this.fJobsByName.clear(); this.fTriggersByName.clear(); this.fCalendarsByName.clear(); this.fTriggerStatesByName.clear(); this.fPausedTriggerGroups.clear(); this.fJobsByGroup.clear(); this.fTriggersByGroup.clear(); this.fBlockedJobs.clear(); this.fOrderedTriggers.clear(); this.fSerializer.clear(); } private static long ftrCtr = System.currentTimeMillis(); private synchronized String getFiredTriggerRecordId() { return String.valueOf(ftrCtr++); } // -------------------------persistence-related--------------------- private void initPeristentFields() throws JobPersistenceException { this.fJobsByName = (HashMap) this.fSerializer.loadJobs(); this.fTriggersByName = (HashMap) this.fSerializer.loadTriggers(); this.fCalendarsByName = (HashMap) this.fSerializer.loadCalendars(); this.fTriggerStatesByName = (HashMap) this.fSerializer.loadTriggerStates(); this.fPausedTriggerGroups = (HashSet) this.fSerializer.loadPausedTriggerGroups(); if (this.fJobsByName == null) this.fJobsByName = new HashMap(); if (this.fTriggersByName == null) this.fTriggersByName = new HashMap(); if (this.fCalendarsByName == null) this.fCalendarsByName = new HashMap(); if (this.fTriggerStatesByName == null) this.fTriggerStatesByName = new HashMap(); if (this.fPausedTriggerGroups == null) this.fPausedTriggerGroups = new HashSet(); } private void fillTransientFields() { for (Iterator iter = this.fJobsByName.values().iterator(); iter.hasNext();) { JobDetail job = (JobDetail) iter.next(); addJobGroup(job); } for (Iterator iter = this.fTriggersByName.values().iterator(); iter .hasNext();) { Trigger trigger = (Trigger) iter.next(); addTriggerGroup(trigger); } this.fOrderedTriggers.addAll(this.fTriggersByName.values()); } }