Java tutorial
/* * Copyright 2001-2009 James House * * Licensed under the 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.jdbcjobstore; 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; import java.sql.Blob; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; 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.Properties; import java.util.Set; import java.util.TimeZone; 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.spi.ClassLoadHelper; import org.quartz.utils.Key; import org.quartz.utils.TriggerStatus; /** * <p> * This is meant to be an abstract base class for most, if not all, <code>{@link org.quartz.impl.jdbcjobstore.DriverDelegate}</code> * implementations. Subclasses should override only those methods that need * special handling for the DBMS driver in question. * </p> * * @author <a href="mailto:jeff@binaryfeed.org">Jeffrey Wescott</a> * @author James House * @author Eric Mueller */ public class StdJDBCDelegate implements DriverDelegate, StdJDBCConstants { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Data members. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ protected Log logger = null; protected String tablePrefix = DEFAULT_TABLE_PREFIX; protected String instanceId; protected boolean useProperties; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Constructors. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * <p> * Create new StdJDBCDelegate instance. * </p> * * @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; } /** * <p> * Create new StdJDBCDelegate instance. * </p> * * @param logger * the logger to use during execution * @param tablePrefix * the prefix of all table names */ public StdJDBCDelegate(Log logger, String tablePrefix, String instanceId, Boolean useProperties) { this.logger = logger; this.tablePrefix = tablePrefix; this.instanceId = instanceId; this.useProperties = useProperties.booleanValue(); } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Interface. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ protected boolean canUseProperties() { return useProperties; } //--------------------------------------------------------------------------- // startup / recovery //--------------------------------------------------------------------------- /** * <p> * Insert the job detail record. * </p> * * @param conn * the DB Connection * @param newState * the new state for the triggers * @param oldState1 * the first old state to update * @param oldState2 * the second old state to update * @return number of rows updated */ public int updateTriggerStatesFromOtherStates(Connection conn, String newState, String oldState1, String oldState2) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_STATES_FROM_OTHER_STATES)); ps.setString(1, newState); ps.setString(2, oldState1); ps.setString(3, oldState2); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Get the names of all of the triggers that have misfired. * </p> * * @param conn * the DB Connection * @return an array of <code>{@link * org.quartz.utils.Key}</code> objects */ public Key[] selectMisfiredTriggers(Connection conn, long ts) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS)); ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts))); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { String triggerName = rs.getString(COL_TRIGGER_NAME); String groupName = rs.getString(COL_TRIGGER_GROUP); list.add(new Key(triggerName, groupName)); } Object[] oArr = list.toArray(); Key[] kArr = new Key[oArr.length]; System.arraycopy(oArr, 0, kArr, 0, oArr.length); return kArr; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select all of the triggers in a given state. * </p> * * @param conn * the DB Connection * @param state * the state the triggers must be in * @return an array of trigger <code>Key</code> s */ public Key[] selectTriggersInState(Connection conn, String state) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_IN_STATE)); ps.setString(1, state); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { list.add(new Key(rs.getString(1), rs.getString(2))); } Key[] sArr = (Key[]) list.toArray(new Key[list.size()]); return sArr; } finally { closeResultSet(rs); closeStatement(ps); } } public Key[] selectMisfiredTriggersInState(Connection conn, String state, long ts) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS_IN_STATE)); ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts))); ps.setString(2, state); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { String triggerName = rs.getString(COL_TRIGGER_NAME); String groupName = rs.getString(COL_TRIGGER_GROUP); list.add(new Key(triggerName, groupName)); } Object[] oArr = list.toArray(); Key[] kArr = new Key[oArr.length]; System.arraycopy(oArr, 0, kArr, 0, oArr.length); return kArr; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * 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. * </p> * * @param conn The DB Connection * @param count The most misfired triggers to return, negative for all * @param resultList Output parameter. A List of * <code>{@link org.quartz.utils.Key}</code> objects. Must not be null. * * @return Whether there are more misfired triggers left to find beyond * the given count. */ public boolean selectMisfiredTriggersInStates(Connection conn, String state1, String state2, long ts, int count, List resultList) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS_IN_STATES)); ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts))); ps.setString(2, state1); ps.setString(3, state2); rs = ps.executeQuery(); boolean hasReachedLimit = false; while (rs.next() && (hasReachedLimit == false)) { if (resultList.size() == count) { hasReachedLimit = true; } else { String triggerName = rs.getString(COL_TRIGGER_NAME); String groupName = rs.getString(COL_TRIGGER_GROUP); resultList.add(new Key(triggerName, groupName)); } } return hasReachedLimit; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Get the number of triggers in the given states that have * misfired - according to the given timestamp. * </p> * * @param conn the DB Connection */ public int countMisfiredTriggersInStates(Connection conn, String state1, String state2, long ts) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(COUNT_MISFIRED_TRIGGERS_IN_STATES)); ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts))); ps.setString(2, state1); ps.setString(3, state2); rs = ps.executeQuery(); if (rs.next()) { return rs.getInt(1); } throw new SQLException("No misfired trigger count returned."); } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Get the names of all of the triggers in the given group and state that * have misfired. * </p> * * @param conn * the DB Connection * @return an array of <code>{@link * org.quartz.utils.Key}</code> objects */ public Key[] selectMisfiredTriggersInGroupInState(Connection conn, String groupName, String state, long ts) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS_IN_GROUP_IN_STATE)); ps.setBigDecimal(1, new BigDecimal(String.valueOf(ts))); ps.setString(2, groupName); ps.setString(3, state); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { String triggerName = rs.getString(COL_TRIGGER_NAME); list.add(new Key(triggerName, groupName)); } Object[] oArr = list.toArray(); Key[] kArr = new Key[oArr.length]; System.arraycopy(oArr, 0, kArr, 0, oArr.length); return kArr; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select all of the triggers for jobs that are requesting recovery. The * returned trigger objects will have unique "recoverXXX" trigger names and * will be in the <code>{@link * org.quartz.Scheduler}.DEFAULT_RECOVERY_GROUP</code> * trigger group. * </p> * * <p> * In order to preserve the ordering of the triggers, the fire time will be * set from the <code>COL_FIRED_TIME</code> column in the <code>TABLE_FIRED_TRIGGERS</code> * table. The caller is responsible for calling <code>computeFirstFireTime</code> * on each returned trigger. It is also up to the caller to insert the * returned triggers to ensure that they are fired. * </p> * * @param conn * the DB Connection * @return an array of <code>{@link org.quartz.Trigger}</code> objects */ public Trigger[] selectTriggersForRecoveringJobs(Connection conn) throws SQLException, IOException, ClassNotFoundException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_INSTANCES_RECOVERABLE_FIRED_TRIGGERS)); ps.setString(1, instanceId); setBoolean(ps, 2, true); rs = ps.executeQuery(); long dumId = System.currentTimeMillis(); ArrayList list = new ArrayList(); while (rs.next()) { String jobName = rs.getString(COL_JOB_NAME); String jobGroup = rs.getString(COL_JOB_GROUP); String trigName = rs.getString(COL_TRIGGER_NAME); String trigGroup = rs.getString(COL_TRIGGER_GROUP); long firedTime = rs.getLong(COL_FIRED_TIME); int priority = rs.getInt(COL_PRIORITY); SimpleTrigger rcvryTrig = new SimpleTrigger("recover_" + instanceId + "_" + String.valueOf(dumId++), Scheduler.DEFAULT_RECOVERY_GROUP, new Date(firedTime)); rcvryTrig.setJobName(jobName); rcvryTrig.setJobGroup(jobGroup); rcvryTrig.setPriority(priority); rcvryTrig.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW); JobDataMap jd = selectTriggerJobDataMap(conn, trigName, trigGroup); jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_NAME, trigName); jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_GROUP, trigGroup); jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_FIRETIME_IN_MILLISECONDS, String.valueOf(firedTime)); rcvryTrig.setJobDataMap(jd); list.add(rcvryTrig); } Object[] oArr = list.toArray(); Trigger[] tArr = new Trigger[oArr.length]; System.arraycopy(oArr, 0, tArr, 0, oArr.length); return tArr; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Delete all fired triggers. * </p> * * @param conn * the DB Connection * @return the number of rows deleted */ public int deleteFiredTriggers(Connection conn) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_FIRED_TRIGGERS)); return ps.executeUpdate(); } finally { closeStatement(ps); } } public int deleteFiredTriggers(Connection conn, String instanceId) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_INSTANCES_FIRED_TRIGGERS)); ps.setString(1, instanceId); return ps.executeUpdate(); } finally { closeStatement(ps); } } //--------------------------------------------------------------------------- // jobs //--------------------------------------------------------------------------- /** * <p> * Insert the job detail record. * </p> * * @param conn * the DB Connection * @param job * the job to insert * @return number of rows inserted * @throws IOException * if there were problems serializing the JobDataMap */ public int insertJobDetail(Connection conn, JobDetail job) throws IOException, SQLException { ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); PreparedStatement ps = null; int insertResult = 0; try { ps = conn.prepareStatement(rtp(INSERT_JOB_DETAIL)); ps.setString(1, job.getName()); ps.setString(2, job.getGroup()); ps.setString(3, job.getDescription()); ps.setString(4, job.getJobClass().getName()); setBoolean(ps, 5, job.isDurable()); setBoolean(ps, 6, job.isVolatile()); setBoolean(ps, 7, job.isStateful()); setBoolean(ps, 8, job.requestsRecovery()); setBytes(ps, 9, baos); insertResult = ps.executeUpdate(); } finally { closeStatement(ps); } if (insertResult > 0) { String[] jobListeners = job.getJobListenerNames(); for (int i = 0; jobListeners != null && i < jobListeners.length; i++) { insertJobListener(conn, job, jobListeners[i]); } } return insertResult; } /** * <p> * Update the job detail record. * </p> * * @param conn * the DB Connection * @param job * the job to update * @return number of rows updated * @throws IOException * if there were problems serializing the JobDataMap */ public int updateJobDetail(Connection conn, JobDetail job) throws IOException, SQLException { ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); PreparedStatement ps = null; int insertResult = 0; try { ps = conn.prepareStatement(rtp(UPDATE_JOB_DETAIL)); ps.setString(1, job.getDescription()); ps.setString(2, job.getJobClass().getName()); setBoolean(ps, 3, job.isDurable()); setBoolean(ps, 4, job.isVolatile()); setBoolean(ps, 5, job.isStateful()); setBoolean(ps, 6, job.requestsRecovery()); setBytes(ps, 7, baos); ps.setString(8, job.getName()); ps.setString(9, job.getGroup()); insertResult = ps.executeUpdate(); } finally { closeStatement(ps); } if (insertResult > 0) { deleteJobListeners(conn, job.getName(), job.getGroup()); String[] jobListeners = job.getJobListenerNames(); for (int i = 0; jobListeners != null && i < jobListeners.length; i++) { insertJobListener(conn, job, jobListeners[i]); } } return insertResult; } /** * <p> * Get the names of all of the triggers associated with the given job. * </p> * * @param conn * the DB Connection * @param jobName * the name of the job * @param groupName * the group containing the job * @return an array of <code>{@link * org.quartz.utils.Key}</code> objects */ public Key[] selectTriggerNamesForJob(Connection conn, String jobName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_FOR_JOB)); ps.setString(1, jobName); ps.setString(2, groupName); rs = ps.executeQuery(); ArrayList list = new ArrayList(10); while (rs.next()) { String trigName = rs.getString(COL_TRIGGER_NAME); String trigGroup = rs.getString(COL_TRIGGER_GROUP); list.add(new Key(trigName, trigGroup)); } Object[] oArr = list.toArray(); Key[] kArr = new Key[oArr.length]; System.arraycopy(oArr, 0, kArr, 0, oArr.length); return kArr; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Delete all job listeners for the given job. * </p> * * @param conn * the DB Connection * @param jobName * the name of the job * @param groupName * the group containing the job * @return the number of rows deleted */ public int deleteJobListeners(Connection conn, String jobName, String groupName) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_JOB_LISTENERS)); ps.setString(1, jobName); ps.setString(2, groupName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Delete the job detail record for the given job. * </p> * * @param conn * the DB Connection * @param jobName * the name of the job * @param groupName * the group containing the job * @return the number of rows deleted */ public int deleteJobDetail(Connection conn, String jobName, String groupName) throws SQLException { PreparedStatement ps = null; try { if (logger.isDebugEnabled()) { logger.debug("Deleting job: " + groupName + "." + jobName); } ps = conn.prepareStatement(rtp(DELETE_JOB_DETAIL)); ps.setString(1, jobName); ps.setString(2, groupName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Check whether or not the given job is stateful. * </p> * * @param conn * the DB Connection * @param jobName * the name of the job * @param groupName * the group containing the job * @return true if the job exists and is stateful, false otherwise */ public boolean isJobStateful(Connection conn, String jobName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_JOB_STATEFUL)); ps.setString(1, jobName); ps.setString(2, groupName); rs = ps.executeQuery(); if (!rs.next()) { return false; } return getBoolean(rs, COL_IS_STATEFUL); } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Check whether or not the given job exists. * </p> * * @param conn * the DB Connection * @param jobName * the name of the job * @param groupName * the group containing the job * @return true if the job exists, false otherwise */ public boolean jobExists(Connection conn, String jobName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_JOB_EXISTENCE)); ps.setString(1, jobName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { return true; } else { return false; } } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Update the job data map for the given job. * </p> * * @param conn * the DB Connection * @param job * the job to update * @return the number of rows updated */ public int updateJobData(Connection conn, JobDetail job) throws IOException, SQLException { ByteArrayOutputStream baos = serializeJobData(job.getJobDataMap()); PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_JOB_DATA)); setBytes(ps, 1, baos); ps.setString(2, job.getName()); ps.setString(3, job.getGroup()); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Associate a listener with a job. * </p> * * @param conn * the DB Connection * @param job * the job to associate with the listener * @param listener * the listener to insert * @return the number of rows inserted */ public int insertJobListener(Connection conn, JobDetail job, String listener) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(INSERT_JOB_LISTENER)); ps.setString(1, job.getName()); ps.setString(2, job.getGroup()); ps.setString(3, listener); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Get all of the listeners for a given job. * </p> * * @param conn * the DB Connection * @param jobName * the job name whose listeners are wanted * @param groupName * the group containing the job * @return array of <code>String</code> listener names */ public String[] selectJobListeners(Connection conn, String jobName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ArrayList list = new ArrayList(); ps = conn.prepareStatement(rtp(SELECT_JOB_LISTENERS)); ps.setString(1, jobName); ps.setString(2, groupName); rs = ps.executeQuery(); while (rs.next()) { list.add(rs.getString(1)); } Object[] oArr = list.toArray(); String[] sArr = new String[oArr.length]; System.arraycopy(oArr, 0, sArr, 0, oArr.length); return sArr; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select the JobDetail object for a given job name / group name. * </p> * * @param conn * the DB Connection * @param jobName * the job name whose listeners are wanted * @param groupName * the group containing the job * @return the populated JobDetail object * @throws ClassNotFoundException * if a class found during deserialization cannot be found or if * the job class could not be found * @throws IOException * if deserialization causes an error */ public JobDetail selectJobDetail(Connection conn, String jobName, String groupName, ClassLoadHelper loadHelper) throws ClassNotFoundException, IOException, SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_JOB_DETAIL)); ps.setString(1, jobName); ps.setString(2, groupName); rs = ps.executeQuery(); JobDetail job = null; if (rs.next()) { job = new JobDetail(); job.setName(rs.getString(COL_JOB_NAME)); job.setGroup(rs.getString(COL_JOB_GROUP)); job.setDescription(rs.getString(COL_DESCRIPTION)); job.setJobClass(loadHelper.loadClass(rs.getString(COL_JOB_CLASS))); job.setDurability(getBoolean(rs, COL_IS_DURABLE)); job.setVolatility(getBoolean(rs, COL_IS_VOLATILE)); job.setRequestsRecovery(getBoolean(rs, COL_REQUESTS_RECOVERY)); Map map = null; if (canUseProperties()) { map = getMapFromProperties(rs); } else { map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP); } if (null != map) { job.setJobDataMap(new JobDataMap(map)); } } return job; } finally { closeResultSet(rs); closeStatement(ps); } } /** * build Map from java.util.Properties encoding. */ private Map getMapFromProperties(ResultSet rs) throws ClassNotFoundException, IOException, SQLException { Map map; InputStream is = (InputStream) getJobDetailFromBlob(rs, COL_JOB_DATAMAP); if (is == null) { return null; } Properties properties = new Properties(); if (is != null) { try { properties.load(is); } finally { is.close(); } } map = convertFromProperty(properties); return map; } /** * <p> * Select the total number of jobs stored. * </p> * * @param conn * the DB Connection * @return the total number of jobs stored */ public int selectNumJobs(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { int count = 0; ps = conn.prepareStatement(rtp(SELECT_NUM_JOBS)); rs = ps.executeQuery(); if (rs.next()) { count = rs.getInt(1); } return count; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select all of the job group names that are stored. * </p> * * @param conn * the DB Connection * @return an array of <code>String</code> group names */ public String[] selectJobGroups(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_JOB_GROUPS)); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { list.add(rs.getString(1)); } Object[] oArr = list.toArray(); String[] sArr = new String[oArr.length]; System.arraycopy(oArr, 0, sArr, 0, oArr.length); return sArr; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select all of the jobs contained in a given group. * </p> * * @param conn * the DB Connection * @param groupName * the group containing the jobs * @return an array of <code>String</code> job names */ public String[] selectJobsInGroup(Connection conn, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_JOBS_IN_GROUP)); ps.setString(1, groupName); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { list.add(rs.getString(1)); } Object[] oArr = list.toArray(); String[] sArr = new String[oArr.length]; System.arraycopy(oArr, 0, sArr, 0, oArr.length); return sArr; } finally { closeResultSet(rs); closeStatement(ps); } } //--------------------------------------------------------------------------- // triggers //--------------------------------------------------------------------------- /** * <p> * Insert the base trigger data. * </p> * * @param conn * the DB Connection * @param trigger * the trigger to insert * @param state * the state that the trigger should be stored in * @return the number of rows inserted */ public int insertTrigger(Connection conn, Trigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { ByteArrayOutputStream baos = null; if (trigger.getJobDataMap().size() > 0) { baos = serializeJobData(trigger.getJobDataMap()); } PreparedStatement ps = null; int insertResult = 0; try { ps = conn.prepareStatement(rtp(INSERT_TRIGGER)); ps.setString(1, trigger.getName()); ps.setString(2, trigger.getGroup()); ps.setString(3, trigger.getJobName()); ps.setString(4, trigger.getJobGroup()); setBoolean(ps, 5, trigger.isVolatile()); ps.setString(6, trigger.getDescription()); if (trigger.getNextFireTime() != null) ps.setBigDecimal(7, new BigDecimal(String.valueOf(trigger.getNextFireTime().getTime()))); else ps.setBigDecimal(7, null); long prevFireTime = -1; if (trigger.getPreviousFireTime() != null) { prevFireTime = trigger.getPreviousFireTime().getTime(); } ps.setBigDecimal(8, new BigDecimal(String.valueOf(prevFireTime))); ps.setString(9, state); if (trigger instanceof SimpleTrigger && ((SimpleTrigger) trigger).hasAdditionalProperties() == false) { ps.setString(10, TTYPE_SIMPLE); } else if (trigger instanceof CronTrigger && ((CronTrigger) trigger).hasAdditionalProperties() == false) { ps.setString(10, TTYPE_CRON); } else { ps.setString(10, TTYPE_BLOB); } ps.setBigDecimal(11, new BigDecimal(String.valueOf(trigger.getStartTime().getTime()))); long endTime = 0; if (trigger.getEndTime() != null) { endTime = trigger.getEndTime().getTime(); } ps.setBigDecimal(12, new BigDecimal(String.valueOf(endTime))); ps.setString(13, trigger.getCalendarName()); ps.setInt(14, trigger.getMisfireInstruction()); setBytes(ps, 15, baos); ps.setInt(16, trigger.getPriority()); insertResult = ps.executeUpdate(); } finally { closeStatement(ps); } if (insertResult > 0) { String[] trigListeners = trigger.getTriggerListenerNames(); for (int i = 0; trigListeners != null && i < trigListeners.length; i++) { insertTriggerListener(conn, trigger, trigListeners[i]); } } return insertResult; } /** * <p> * Insert the simple trigger data. * </p> * * @param conn * the DB Connection * @param trigger * the trigger to insert * @return the number of rows inserted */ public int insertSimpleTrigger(Connection conn, SimpleTrigger trigger) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(INSERT_SIMPLE_TRIGGER)); ps.setString(1, trigger.getName()); ps.setString(2, trigger.getGroup()); ps.setInt(3, trigger.getRepeatCount()); ps.setBigDecimal(4, new BigDecimal(String.valueOf(trigger.getRepeatInterval()))); ps.setInt(5, trigger.getTimesTriggered()); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Insert the cron trigger data. * </p> * * @param conn * the DB Connection * @param trigger * the trigger to insert * @return the number of rows inserted */ public int insertCronTrigger(Connection conn, CronTrigger trigger) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(INSERT_CRON_TRIGGER)); ps.setString(1, trigger.getName()); ps.setString(2, trigger.getGroup()); ps.setString(3, trigger.getCronExpression()); ps.setString(4, trigger.getTimeZone().getID()); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Insert the blob trigger data. * </p> * * @param conn * the DB Connection * @param trigger * the trigger to insert * @return the number of rows inserted */ public int insertBlobTrigger(Connection conn, Trigger trigger) throws SQLException, IOException { PreparedStatement ps = null; ByteArrayOutputStream os = null; try { // update the blob os = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(trigger); oos.close(); byte[] buf = os.toByteArray(); ByteArrayInputStream is = new ByteArrayInputStream(buf); ps = conn.prepareStatement(rtp(INSERT_BLOB_TRIGGER)); ps.setString(1, trigger.getName()); ps.setString(2, trigger.getGroup()); ps.setBinaryStream(3, is, buf.length); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Update the base trigger data. * </p> * * @param conn * the DB Connection * @param trigger * the trigger to insert * @param state * the state that the trigger should be stored in * @return the number of rows updated */ public int updateTrigger(Connection conn, Trigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException { // save some clock cycles by unnecessarily writing job data blob ... boolean updateJobData = trigger.getJobDataMap().isDirty(); ByteArrayOutputStream baos = null; if (updateJobData && trigger.getJobDataMap().size() > 0) { baos = serializeJobData(trigger.getJobDataMap()); } PreparedStatement ps = null; int insertResult = 0; try { if (updateJobData) { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER)); } else { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_SKIP_DATA)); } ps.setString(1, trigger.getJobName()); ps.setString(2, trigger.getJobGroup()); setBoolean(ps, 3, trigger.isVolatile()); ps.setString(4, trigger.getDescription()); long nextFireTime = -1; if (trigger.getNextFireTime() != null) { nextFireTime = trigger.getNextFireTime().getTime(); } ps.setBigDecimal(5, new BigDecimal(String.valueOf(nextFireTime))); long prevFireTime = -1; if (trigger.getPreviousFireTime() != null) { prevFireTime = trigger.getPreviousFireTime().getTime(); } ps.setBigDecimal(6, new BigDecimal(String.valueOf(prevFireTime))); ps.setString(7, state); if (trigger instanceof SimpleTrigger && ((SimpleTrigger) trigger).hasAdditionalProperties() == false) { // updateSimpleTrigger(conn, (SimpleTrigger)trigger); ps.setString(8, TTYPE_SIMPLE); } else if (trigger instanceof CronTrigger && ((CronTrigger) trigger).hasAdditionalProperties() == false) { // updateCronTrigger(conn, (CronTrigger)trigger); ps.setString(8, TTYPE_CRON); } else { // updateBlobTrigger(conn, trigger); ps.setString(8, TTYPE_BLOB); } ps.setBigDecimal(9, new BigDecimal(String.valueOf(trigger.getStartTime().getTime()))); long endTime = 0; if (trigger.getEndTime() != null) { endTime = trigger.getEndTime().getTime(); } ps.setBigDecimal(10, new BigDecimal(String.valueOf(endTime))); ps.setString(11, trigger.getCalendarName()); ps.setInt(12, trigger.getMisfireInstruction()); ps.setInt(13, trigger.getPriority()); if (updateJobData) { setBytes(ps, 14, baos); ps.setString(15, trigger.getName()); ps.setString(16, trigger.getGroup()); } else { ps.setString(14, trigger.getName()); ps.setString(15, trigger.getGroup()); } insertResult = ps.executeUpdate(); } finally { closeStatement(ps); } if (insertResult > 0) { deleteTriggerListeners(conn, trigger.getName(), trigger.getGroup()); String[] trigListeners = trigger.getTriggerListenerNames(); for (int i = 0; trigListeners != null && i < trigListeners.length; i++) { insertTriggerListener(conn, trigger, trigListeners[i]); } } return insertResult; } /** * <p> * Update the simple trigger data. * </p> * * @param conn * the DB Connection * @param trigger * the trigger to insert * @return the number of rows updated */ public int updateSimpleTrigger(Connection conn, SimpleTrigger trigger) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_SIMPLE_TRIGGER)); ps.setInt(1, trigger.getRepeatCount()); ps.setBigDecimal(2, new BigDecimal(String.valueOf(trigger.getRepeatInterval()))); ps.setInt(3, trigger.getTimesTriggered()); ps.setString(4, trigger.getName()); ps.setString(5, trigger.getGroup()); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Update the cron trigger data. * </p> * * @param conn * the DB Connection * @param trigger * the trigger to insert * @return the number of rows updated */ public int updateCronTrigger(Connection conn, CronTrigger trigger) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_CRON_TRIGGER)); ps.setString(1, trigger.getCronExpression()); ps.setString(2, trigger.getName()); ps.setString(3, trigger.getGroup()); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Update the blob trigger data. * </p> * * @param conn * the DB Connection * @param trigger * the trigger to insert * @return the number of rows updated */ public int updateBlobTrigger(Connection conn, Trigger trigger) throws SQLException, IOException { PreparedStatement ps = null; ByteArrayOutputStream os = null; try { // update the blob os = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(trigger); oos.close(); byte[] buf = os.toByteArray(); ByteArrayInputStream is = new ByteArrayInputStream(buf); ps = conn.prepareStatement(rtp(UPDATE_BLOB_TRIGGER)); ps.setBinaryStream(1, is, buf.length); ps.setString(2, trigger.getName()); ps.setString(3, trigger.getGroup()); return ps.executeUpdate(); } finally { closeStatement(ps); if (os != null) { os.close(); } } } /** * <p> * Check whether or not a trigger exists. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return true if the trigger exists, false otherwise */ public boolean triggerExists(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGER_EXISTENCE)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { return true; } else { return false; } } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Update the state for a given trigger. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @param state * the new state for the trigger * @return the number of rows updated */ public int updateTriggerState(Connection conn, String triggerName, String groupName, String state) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_STATE)); ps.setString(1, state); ps.setString(2, triggerName); ps.setString(3, groupName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Update the given trigger to the given new state, if it is one of the * given old states. * </p> * * @param conn * the DB connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @param newState * the new state for the trigger * @param oldState1 * one of the old state the trigger must be in * @param oldState2 * one of the old state the trigger must be in * @param oldState3 * one of the old state the trigger must be in * @return int the number of rows updated * @throws SQLException */ public int updateTriggerStateFromOtherStates(Connection conn, String triggerName, String groupName, String newState, String oldState1, String oldState2, String oldState3) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_STATE_FROM_STATES)); ps.setString(1, newState); ps.setString(2, triggerName); ps.setString(3, groupName); ps.setString(4, oldState1); ps.setString(5, oldState2); ps.setString(6, oldState3); return ps.executeUpdate(); } finally { closeStatement(ps); } } public int updateTriggerStateFromOtherStatesBeforeTime(Connection conn, String newState, String oldState1, String oldState2, long time) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_STATE_FROM_OTHER_STATES_BEFORE_TIME)); ps.setString(1, newState); ps.setString(2, oldState1); ps.setString(3, oldState2); ps.setLong(4, time); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Update all triggers in the given group to the given new state, if they * are in one of the given old states. * </p> * * @param conn * the DB connection * @param groupName * the group containing the trigger * @param newState * the new state for the trigger * @param oldState1 * one of the old state the trigger must be in * @param oldState2 * one of the old state the trigger must be in * @param oldState3 * one of the old state the trigger must be in * @return int the number of rows updated * @throws SQLException */ public int updateTriggerGroupStateFromOtherStates(Connection conn, String groupName, String newState, String oldState1, String oldState2, String oldState3) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_GROUP_STATE_FROM_STATES)); ps.setString(1, newState); ps.setString(2, groupName); ps.setString(3, oldState1); ps.setString(4, oldState2); ps.setString(5, oldState3); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Update the given trigger to the given new state, if it is in the given * old state. * </p> * * @param conn * the DB connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @param newState * the new state for the trigger * @param oldState * the old state the trigger must be in * @return int the number of rows updated * @throws SQLException */ public int updateTriggerStateFromOtherState(Connection conn, String triggerName, String groupName, String newState, String oldState) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_STATE_FROM_STATE)); ps.setString(1, newState); ps.setString(2, triggerName); ps.setString(3, groupName); ps.setString(4, oldState); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Update all of the triggers of the given group to the given new state, if * they are in the given old state. * </p> * * @param conn * the DB connection * @param groupName * the group containing the triggers * @param newState * the new state for the trigger group * @param oldState * the old state the triggers must be in * @return int the number of rows updated * @throws SQLException */ public int updateTriggerGroupStateFromOtherState(Connection conn, String groupName, String newState, String oldState) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_TRIGGER_GROUP_STATE_FROM_STATE)); ps.setString(1, newState); ps.setString(2, groupName); ps.setString(3, oldState); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Update the states of all triggers associated with the given job. * </p> * * @param conn * the DB Connection * @param jobName * the name of the job * @param groupName * the group containing the job * @param state * the new state for the triggers * @return the number of rows updated */ public int updateTriggerStatesForJob(Connection conn, String jobName, String groupName, String state) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_JOB_TRIGGER_STATES)); ps.setString(1, state); ps.setString(2, jobName); ps.setString(3, groupName); return ps.executeUpdate(); } finally { closeStatement(ps); } } public int updateTriggerStatesForJobFromOtherState(Connection conn, String jobName, String groupName, String state, String oldState) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_JOB_TRIGGER_STATES_FROM_OTHER_STATE)); ps.setString(1, state); ps.setString(2, jobName); ps.setString(3, groupName); ps.setString(4, oldState); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Delete all of the listeners associated with a given trigger. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger whose listeners will be deleted * @param groupName * the name of the group containing the trigger * @return the number of rows deleted */ public int deleteTriggerListeners(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_TRIGGER_LISTENERS)); ps.setString(1, triggerName); ps.setString(2, groupName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Associate a listener with the given trigger. * </p> * * @param conn * the DB Connection * @param trigger * the trigger * @param listener * the name of the listener to associate with the trigger * @return the number of rows inserted */ public int insertTriggerListener(Connection conn, Trigger trigger, String listener) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(INSERT_TRIGGER_LISTENER)); ps.setString(1, trigger.getName()); ps.setString(2, trigger.getGroup()); ps.setString(3, listener); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Select the listeners associated with a given trigger. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return array of <code>String</code> trigger listener names */ public String[] selectTriggerListeners(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGER_LISTENERS)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { list.add(rs.getString(1)); } Object[] oArr = list.toArray(); String[] sArr = new String[oArr.length]; System.arraycopy(oArr, 0, sArr, 0, oArr.length); return sArr; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Delete the simple trigger data for a trigger. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return the number of rows deleted */ public int deleteSimpleTrigger(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_SIMPLE_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Delete the cron trigger data for a trigger. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return the number of rows deleted */ public int deleteCronTrigger(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_CRON_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Delete the cron trigger data for a trigger. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return the number of rows deleted */ public int deleteBlobTrigger(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_BLOB_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Delete the base trigger data for a trigger. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return the number of rows deleted */ public int deleteTrigger(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Select the number of triggers associated with a given job. * </p> * * @param conn * the DB Connection * @param jobName * the name of the job * @param groupName * the group containing the job * @return the number of triggers for the given job */ public int selectNumTriggersForJob(Connection conn, String jobName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_NUM_TRIGGERS_FOR_JOB)); ps.setString(1, jobName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { return rs.getInt(1); } else { return 0; } } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select the job to which the trigger is associated. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return the <code>{@link org.quartz.JobDetail}</code> object * associated with the given trigger * @throws SQLException * @throws ClassNotFoundException */ public JobDetail selectJobForTrigger(Connection conn, String triggerName, String groupName, ClassLoadHelper loadHelper) throws ClassNotFoundException, SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_JOB_FOR_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { JobDetail job = new JobDetail(); job.setName(rs.getString(1)); job.setGroup(rs.getString(2)); job.setDurability(getBoolean(rs, 3)); job.setJobClass(loadHelper.loadClass(rs.getString(4))); job.setRequestsRecovery(getBoolean(rs, 5)); return job; } else { if (logger.isDebugEnabled()) { logger.debug("No job for trigger '" + groupName + "." + triggerName + "'."); } return null; } } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select the triggers for a job * </p> * * @param conn * the DB Connection * @param jobName * the name of the trigger * @param groupName * the group containing the trigger * @return an array of <code>(@link org.quartz.Trigger)</code> objects * associated with a given job. * @throws SQLException */ public Trigger[] selectTriggersForJob(Connection conn, String jobName, String groupName) throws SQLException, ClassNotFoundException, IOException { ArrayList trigList = new ArrayList(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_FOR_JOB)); ps.setString(1, jobName); ps.setString(2, groupName); rs = ps.executeQuery(); while (rs.next()) { Trigger t = selectTrigger(conn, rs.getString(COL_TRIGGER_NAME), rs.getString(COL_TRIGGER_GROUP)); if (t != null) { trigList.add(t); } } } finally { closeResultSet(rs); closeStatement(ps); } return (Trigger[]) trigList.toArray(new Trigger[trigList.size()]); } public Trigger[] selectTriggersForCalendar(Connection conn, String calName) throws SQLException, ClassNotFoundException, IOException { ArrayList trigList = new ArrayList(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_FOR_CALENDAR)); ps.setString(1, calName); rs = ps.executeQuery(); while (rs.next()) { trigList.add(selectTrigger(conn, rs.getString(COL_TRIGGER_NAME), rs.getString(COL_TRIGGER_GROUP))); } } finally { closeResultSet(rs); closeStatement(ps); } return (Trigger[]) trigList.toArray(new Trigger[trigList.size()]); } public List selectStatefulJobsOfTriggerGroup(Connection conn, String groupName) throws SQLException { ArrayList jobList = new ArrayList(); PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_STATEFUL_JOBS_OF_TRIGGER_GROUP)); ps.setString(1, groupName); setBoolean(ps, 2, true); rs = ps.executeQuery(); while (rs.next()) { jobList.add(new Key(rs.getString(COL_JOB_NAME), rs.getString(COL_JOB_GROUP))); } } finally { closeResultSet(rs); closeStatement(ps); } return jobList; } /** * <p> * Select a trigger. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return the <code>{@link org.quartz.Trigger}</code> object */ public Trigger selectTrigger(Connection conn, String triggerName, String groupName) throws SQLException, ClassNotFoundException, IOException { PreparedStatement ps = null; ResultSet rs = null; try { Trigger trigger = null; ps = conn.prepareStatement(rtp(SELECT_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { String jobName = rs.getString(COL_JOB_NAME); String jobGroup = rs.getString(COL_JOB_GROUP); boolean volatility = getBoolean(rs, COL_IS_VOLATILE); String description = rs.getString(COL_DESCRIPTION); long nextFireTime = rs.getLong(COL_NEXT_FIRE_TIME); long prevFireTime = rs.getLong(COL_PREV_FIRE_TIME); String triggerType = rs.getString(COL_TRIGGER_TYPE); long startTime = rs.getLong(COL_START_TIME); long endTime = rs.getLong(COL_END_TIME); String calendarName = rs.getString(COL_CALENDAR_NAME); int misFireInstr = rs.getInt(COL_MISFIRE_INSTRUCTION); int priority = rs.getInt(COL_PRIORITY); Map map = null; if (canUseProperties()) { map = getMapFromProperties(rs); } else { map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP); } Date nft = null; if (nextFireTime > 0) { nft = new Date(nextFireTime); } Date pft = null; if (prevFireTime > 0) { pft = new Date(prevFireTime); } Date startTimeD = new Date(startTime); Date endTimeD = null; if (endTime > 0) { endTimeD = new Date(endTime); } rs.close(); ps.close(); if (triggerType.equals(TTYPE_SIMPLE)) { ps = conn.prepareStatement(rtp(SELECT_SIMPLE_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { int repeatCount = rs.getInt(COL_REPEAT_COUNT); long repeatInterval = rs.getLong(COL_REPEAT_INTERVAL); int timesTriggered = rs.getInt(COL_TIMES_TRIGGERED); SimpleTrigger st = new SimpleTrigger(triggerName, groupName, jobName, jobGroup, startTimeD, endTimeD, repeatCount, repeatInterval); st.setCalendarName(calendarName); st.setMisfireInstruction(misFireInstr); st.setTimesTriggered(timesTriggered); st.setVolatility(volatility); st.setNextFireTime(nft); st.setPreviousFireTime(pft); st.setDescription(description); st.setPriority(priority); if (null != map) { st.setJobDataMap(new JobDataMap(map)); } trigger = st; } } else if (triggerType.equals(TTYPE_CRON)) { ps = conn.prepareStatement(rtp(SELECT_CRON_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { String cronExpr = rs.getString(COL_CRON_EXPRESSION); String timeZoneId = rs.getString(COL_TIME_ZONE_ID); CronTrigger ct = null; try { TimeZone timeZone = null; if (timeZoneId != null) { timeZone = TimeZone.getTimeZone(timeZoneId); } ct = new CronTrigger(triggerName, groupName, jobName, jobGroup, startTimeD, endTimeD, cronExpr, timeZone); } catch (Exception neverHappens) { // expr must be valid, or it never would have // gotten to the store... } if (null != ct) { ct.setCalendarName(calendarName); ct.setMisfireInstruction(misFireInstr); ct.setVolatility(volatility); ct.setNextFireTime(nft); ct.setPreviousFireTime(pft); ct.setDescription(description); ct.setPriority(priority); if (null != map) { ct.setJobDataMap(new JobDataMap(map)); } trigger = ct; } } } else if (triggerType.equals(TTYPE_BLOB)) { ps = conn.prepareStatement(rtp(SELECT_BLOB_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { trigger = (Trigger) getObjectFromBlob(rs, COL_BLOB); } } else { throw new ClassNotFoundException("class for trigger type '" + triggerType + "' not found."); } } return trigger; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select a trigger's JobDataMap. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return the <code>{@link org.quartz.JobDataMap}</code> of the Trigger, * never null, but possibly empty. */ public JobDataMap selectTriggerJobDataMap(Connection conn, String triggerName, String groupName) throws SQLException, ClassNotFoundException, IOException { PreparedStatement ps = null; ResultSet rs = null; try { Trigger trigger = null; ps = conn.prepareStatement(rtp(SELECT_TRIGGER_DATA)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { Map map = null; if (canUseProperties()) { map = getMapFromProperties(rs); } else { map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP); } rs.close(); ps.close(); if (null != map) { return new JobDataMap(map); } } } finally { closeResultSet(rs); closeStatement(ps); } return new JobDataMap(); } /** * <p> * Select a trigger' state value. * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return the <code>{@link org.quartz.Trigger}</code> object */ public String selectTriggerState(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { String state = null; ps = conn.prepareStatement(rtp(SELECT_TRIGGER_STATE)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { state = rs.getString(COL_TRIGGER_STATE); } else { state = STATE_DELETED; } return state.intern(); } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select a trigger' status (state & next fire time). * </p> * * @param conn * the DB Connection * @param triggerName * the name of the trigger * @param groupName * the group containing the trigger * @return a <code>TriggerStatus</code> object, or null */ public TriggerStatus selectTriggerStatus(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { TriggerStatus status = null; ps = conn.prepareStatement(rtp(SELECT_TRIGGER_STATUS)); ps.setString(1, triggerName); ps.setString(2, groupName); rs = ps.executeQuery(); if (rs.next()) { String state = rs.getString(COL_TRIGGER_STATE); long nextFireTime = rs.getLong(COL_NEXT_FIRE_TIME); String jobName = rs.getString(COL_JOB_NAME); String jobGroup = rs.getString(COL_JOB_GROUP); Date nft = null; if (nextFireTime > 0) { nft = new Date(nextFireTime); } status = new TriggerStatus(state, nft); status.setKey(new Key(triggerName, groupName)); status.setJobKey(new Key(jobName, jobGroup)); } return status; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select the total number of triggers stored. * </p> * * @param conn * the DB Connection * @return the total number of triggers stored */ public int selectNumTriggers(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { int count = 0; ps = conn.prepareStatement(rtp(SELECT_NUM_TRIGGERS)); rs = ps.executeQuery(); if (rs.next()) { count = rs.getInt(1); } return count; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select all of the trigger group names that are stored. * </p> * * @param conn * the DB Connection * @return an array of <code>String</code> group names */ public String[] selectTriggerGroups(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGER_GROUPS)); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { list.add(rs.getString(1)); } Object[] oArr = list.toArray(); String[] sArr = new String[oArr.length]; System.arraycopy(oArr, 0, sArr, 0, oArr.length); return sArr; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select all of the triggers contained in a given group. * </p> * * @param conn * the DB Connection * @param groupName * the group containing the triggers * @return an array of <code>String</code> trigger names */ public String[] selectTriggersInGroup(Connection conn, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_IN_GROUP)); ps.setString(1, groupName); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { list.add(rs.getString(1)); } Object[] oArr = list.toArray(); String[] sArr = new String[oArr.length]; System.arraycopy(oArr, 0, sArr, 0, oArr.length); return sArr; } finally { closeResultSet(rs); closeStatement(ps); } } public int insertPausedTriggerGroup(Connection conn, String groupName) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(INSERT_PAUSED_TRIGGER_GROUP)); ps.setString(1, groupName); int rows = ps.executeUpdate(); return rows; } finally { closeStatement(ps); } } public int deletePausedTriggerGroup(Connection conn, String groupName) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_PAUSED_TRIGGER_GROUP)); ps.setString(1, groupName); int rows = ps.executeUpdate(); return rows; } finally { closeStatement(ps); } } public int deleteAllPausedTriggerGroups(Connection conn) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_PAUSED_TRIGGER_GROUPS)); int rows = ps.executeUpdate(); return rows; } finally { closeStatement(ps); } } public boolean isTriggerGroupPaused(Connection conn, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_PAUSED_TRIGGER_GROUP)); ps.setString(1, groupName); rs = ps.executeQuery(); return rs.next(); } finally { closeResultSet(rs); closeStatement(ps); } } public boolean isExistingTriggerGroup(Connection conn, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_NUM_TRIGGERS_IN_GROUP)); ps.setString(1, groupName); rs = ps.executeQuery(); if (!rs.next()) { return false; } return (rs.getInt(1) > 0); } finally { closeResultSet(rs); closeStatement(ps); } } //--------------------------------------------------------------------------- // calendars //--------------------------------------------------------------------------- /** * <p> * Insert a new calendar. * </p> * * @param conn * the DB Connection * @param calendarName * the name for the new calendar * @param calendar * the calendar * @return the number of rows inserted * @throws IOException * if there were problems serializing the calendar */ public int insertCalendar(Connection conn, String calendarName, Calendar calendar) throws IOException, SQLException { ByteArrayOutputStream baos = serializeObject(calendar); PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(INSERT_CALENDAR)); ps.setString(1, calendarName); setBytes(ps, 2, baos); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Update a calendar. * </p> * * @param conn * the DB Connection * @param calendarName * the name for the new calendar * @param calendar * the calendar * @return the number of rows updated * @throws IOException * if there were problems serializing the calendar */ public int updateCalendar(Connection conn, String calendarName, Calendar calendar) throws IOException, SQLException { ByteArrayOutputStream baos = serializeObject(calendar); PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_CALENDAR)); setBytes(ps, 1, baos); ps.setString(2, calendarName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Check whether or not a calendar exists. * </p> * * @param conn * the DB Connection * @param calendarName * the name of the calendar * @return true if the trigger exists, false otherwise */ public boolean calendarExists(Connection conn, String calendarName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_CALENDAR_EXISTENCE)); ps.setString(1, calendarName); rs = ps.executeQuery(); if (rs.next()) { return true; } else { return false; } } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select a calendar. * </p> * * @param conn * the DB Connection * @param calendarName * the name of the calendar * @return the Calendar * @throws ClassNotFoundException * if a class found during deserialization cannot be found be * found * @throws IOException * if there were problems deserializing the calendar */ public Calendar selectCalendar(Connection conn, String calendarName) throws ClassNotFoundException, IOException, SQLException { PreparedStatement ps = null; ResultSet rs = null; try { String selCal = rtp(SELECT_CALENDAR); ps = conn.prepareStatement(selCal); ps.setString(1, calendarName); rs = ps.executeQuery(); Calendar cal = null; if (rs.next()) { cal = (Calendar) getObjectFromBlob(rs, COL_CALENDAR); } if (null == cal) { logger.warn("Couldn't find calendar with name '" + calendarName + "'."); } return cal; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Check whether or not a calendar is referenced by any triggers. * </p> * * @param conn * the DB Connection * @param calendarName * the name of the calendar * @return true if any triggers reference the calendar, false otherwise */ public boolean calendarIsReferenced(Connection conn, String calendarName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_REFERENCED_CALENDAR)); ps.setString(1, calendarName); rs = ps.executeQuery(); if (rs.next()) { return true; } else { return false; } } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Delete a calendar. * </p> * * @param conn * the DB Connection * @param calendarName * the name of the trigger * @return the number of rows deleted */ public int deleteCalendar(Connection conn, String calendarName) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_CALENDAR)); ps.setString(1, calendarName); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Select the total number of calendars stored. * </p> * * @param conn * the DB Connection * @return the total number of calendars stored */ public int selectNumCalendars(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { int count = 0; ps = conn.prepareStatement(rtp(SELECT_NUM_CALENDARS)); rs = ps.executeQuery(); if (rs.next()) { count = rs.getInt(1); } return count; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select all of the stored calendars. * </p> * * @param conn * the DB Connection * @return an array of <code>String</code> calendar names */ public String[] selectCalendars(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_CALENDARS)); rs = ps.executeQuery(); ArrayList list = new ArrayList(); while (rs.next()) { list.add(rs.getString(1)); } Object[] oArr = list.toArray(); String[] sArr = new String[oArr.length]; System.arraycopy(oArr, 0, sArr, 0, oArr.length); return sArr; } finally { closeResultSet(rs); closeStatement(ps); } } //--------------------------------------------------------------------------- // trigger firing //--------------------------------------------------------------------------- /** * <p> * Select the next time that a trigger will be fired. * </p> * * @param conn * the DB Connection * @return the next fire time, or 0 if no trigger will be fired * * @deprecated Does not account for misfires. */ public long selectNextFireTime(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_NEXT_FIRE_TIME)); ps.setString(1, STATE_WAITING); rs = ps.executeQuery(); if (rs.next()) { return rs.getLong(ALIAS_COL_NEXT_FIRE_TIME); } else { return 0l; } } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select the trigger that will be fired at the given fire time. * </p> * * @param conn * the DB Connection * @param fireTime * the time that the trigger will be fired * @return a <code>{@link org.quartz.utils.Key}</code> representing the * trigger that will be fired at the given fire time, or null if no * trigger will be fired at that time */ public Key selectTriggerForFireTime(Connection conn, long fireTime) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_TRIGGER_FOR_FIRE_TIME)); ps.setString(1, STATE_WAITING); ps.setBigDecimal(2, new BigDecimal(String.valueOf(fireTime))); rs = ps.executeQuery(); if (rs.next()) { return new Key(rs.getString(COL_TRIGGER_NAME), rs.getString(COL_TRIGGER_GROUP)); } else { return null; } } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * 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. * </p> * * @param conn * the DB Connection * @param noLaterThan * highest value of <code>getNextFireTime()</code> of the triggers (exclusive) * @param noEarlierThan * highest value of <code>getNextFireTime()</code> of the triggers (inclusive) * * @return A (never null, possibly empty) list of the identifiers (Key objects) of the next triggers to be fired. */ public List selectTriggerToAcquire(Connection conn, long noLaterThan, long noEarlierThan) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; List nextTriggers = new LinkedList(); try { ps = conn.prepareStatement(rtp(SELECT_NEXT_TRIGGER_TO_ACQUIRE)); // Try to give jdbc driver a hint to hopefully not pull over // more than the few rows we actually need. ps.setFetchSize(5); ps.setMaxRows(5); ps.setString(1, STATE_WAITING); ps.setBigDecimal(2, new BigDecimal(String.valueOf(noLaterThan))); ps.setBigDecimal(3, new BigDecimal(String.valueOf(noEarlierThan))); rs = ps.executeQuery(); while (rs.next() && nextTriggers.size() < 5) { nextTriggers.add(new Key(rs.getString(COL_TRIGGER_NAME), rs.getString(COL_TRIGGER_GROUP))); } return nextTriggers; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Insert a fired trigger. * </p> * * @param conn * the DB Connection * @param trigger * the trigger * @param state * the state that the trigger should be stored in * @return the number of rows inserted */ public int insertFiredTrigger(Connection conn, Trigger trigger, String state, JobDetail job) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(INSERT_FIRED_TRIGGER)); ps.setString(1, trigger.getFireInstanceId()); ps.setString(2, trigger.getName()); ps.setString(3, trigger.getGroup()); setBoolean(ps, 4, trigger.isVolatile()); ps.setString(5, instanceId); ps.setBigDecimal(6, new BigDecimal(String.valueOf(trigger.getNextFireTime().getTime()))); ps.setString(7, state); if (job != null) { ps.setString(8, trigger.getJobName()); ps.setString(9, trigger.getJobGroup()); setBoolean(ps, 10, job.isStateful()); setBoolean(ps, 11, job.requestsRecovery()); } else { ps.setString(8, null); ps.setString(9, null); setBoolean(ps, 10, false); setBoolean(ps, 11, false); } ps.setInt(12, trigger.getPriority()); return ps.executeUpdate(); } finally { closeStatement(ps); } } /** * <p> * Select the states of all fired-trigger records for a given trigger, or * trigger group if trigger name is <code>null</code>. * </p> * * @return a List of FiredTriggerRecord objects. */ public List selectFiredTriggerRecords(Connection conn, String triggerName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { List lst = new LinkedList(); if (triggerName != null) { ps = conn.prepareStatement(rtp(SELECT_FIRED_TRIGGER)); ps.setString(1, triggerName); ps.setString(2, groupName); } else { ps = conn.prepareStatement(rtp(SELECT_FIRED_TRIGGER_GROUP)); ps.setString(1, groupName); } rs = ps.executeQuery(); while (rs.next()) { FiredTriggerRecord rec = new FiredTriggerRecord(); rec.setFireInstanceId(rs.getString(COL_ENTRY_ID)); rec.setFireInstanceState(rs.getString(COL_ENTRY_STATE)); rec.setFireTimestamp(rs.getLong(COL_FIRED_TIME)); rec.setPriority(rs.getInt(COL_PRIORITY)); rec.setSchedulerInstanceId(rs.getString(COL_INSTANCE_NAME)); rec.setTriggerIsVolatile(getBoolean(rs, COL_IS_VOLATILE)); rec.setTriggerKey(new Key(rs.getString(COL_TRIGGER_NAME), rs.getString(COL_TRIGGER_GROUP))); if (!rec.getFireInstanceState().equals(STATE_ACQUIRED)) { rec.setJobIsStateful(getBoolean(rs, COL_IS_STATEFUL)); rec.setJobRequestsRecovery(rs.getBoolean(COL_REQUESTS_RECOVERY)); rec.setJobKey(new Key(rs.getString(COL_JOB_NAME), rs.getString(COL_JOB_GROUP))); } lst.add(rec); } return lst; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select the states of all fired-trigger records for a given job, or job * group if job name is <code>null</code>. * </p> * * @return a List of FiredTriggerRecord objects. */ public List selectFiredTriggerRecordsByJob(Connection conn, String jobName, String groupName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { List lst = new LinkedList(); if (jobName != null) { ps = conn.prepareStatement(rtp(SELECT_FIRED_TRIGGERS_OF_JOB)); ps.setString(1, jobName); ps.setString(2, groupName); } else { ps = conn.prepareStatement(rtp(SELECT_FIRED_TRIGGERS_OF_JOB_GROUP)); ps.setString(1, groupName); } rs = ps.executeQuery(); while (rs.next()) { FiredTriggerRecord rec = new FiredTriggerRecord(); rec.setFireInstanceId(rs.getString(COL_ENTRY_ID)); rec.setFireInstanceState(rs.getString(COL_ENTRY_STATE)); rec.setFireTimestamp(rs.getLong(COL_FIRED_TIME)); rec.setPriority(rs.getInt(COL_PRIORITY)); rec.setSchedulerInstanceId(rs.getString(COL_INSTANCE_NAME)); rec.setTriggerIsVolatile(getBoolean(rs, COL_IS_VOLATILE)); rec.setTriggerKey(new Key(rs.getString(COL_TRIGGER_NAME), rs.getString(COL_TRIGGER_GROUP))); if (!rec.getFireInstanceState().equals(STATE_ACQUIRED)) { rec.setJobIsStateful(getBoolean(rs, COL_IS_STATEFUL)); rec.setJobRequestsRecovery(rs.getBoolean(COL_REQUESTS_RECOVERY)); rec.setJobKey(new Key(rs.getString(COL_JOB_NAME), rs.getString(COL_JOB_GROUP))); } lst.add(rec); } return lst; } finally { closeResultSet(rs); closeStatement(ps); } } public List selectInstancesFiredTriggerRecords(Connection conn, String instanceName) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { List lst = new LinkedList(); ps = conn.prepareStatement(rtp(SELECT_INSTANCES_FIRED_TRIGGERS)); ps.setString(1, instanceName); rs = ps.executeQuery(); while (rs.next()) { FiredTriggerRecord rec = new FiredTriggerRecord(); rec.setFireInstanceId(rs.getString(COL_ENTRY_ID)); rec.setFireInstanceState(rs.getString(COL_ENTRY_STATE)); rec.setFireTimestamp(rs.getLong(COL_FIRED_TIME)); rec.setSchedulerInstanceId(rs.getString(COL_INSTANCE_NAME)); rec.setTriggerIsVolatile(getBoolean(rs, COL_IS_VOLATILE)); rec.setTriggerKey(new Key(rs.getString(COL_TRIGGER_NAME), rs.getString(COL_TRIGGER_GROUP))); if (!rec.getFireInstanceState().equals(STATE_ACQUIRED)) { rec.setJobIsStateful(getBoolean(rs, COL_IS_STATEFUL)); rec.setJobRequestsRecovery(rs.getBoolean(COL_REQUESTS_RECOVERY)); rec.setJobKey(new Key(rs.getString(COL_JOB_NAME), rs.getString(COL_JOB_GROUP))); } rec.setPriority(rs.getInt(COL_PRIORITY)); lst.add(rec); } return lst; } finally { closeResultSet(rs); closeStatement(ps); } } /** * <p> * Select the distinct instance names of all fired-trigger records. * </p> * * <p> * This is useful when trying to identify orphaned fired triggers (a * fired trigger without a scheduler state record.) * </p> * * @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); } } /** * <p> * Delete a fired trigger. * </p> * * @param conn * the DB Connection * @param entryId * the fired trigger entry to delete * @return the number of rows deleted */ public int deleteFiredTrigger(Connection conn, String entryId) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_FIRED_TRIGGER)); ps.setString(1, entryId); return ps.executeUpdate(); } finally { closeStatement(ps); } } public int selectJobExecutionCount(Connection conn, String jobName, String jobGroup) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_JOB_EXECUTION_COUNT)); ps.setString(1, jobName); ps.setString(2, jobGroup); rs = ps.executeQuery(); return (rs.next()) ? rs.getInt(1) : 0; } finally { closeResultSet(rs); closeStatement(ps); } } public int deleteVolatileFiredTriggers(Connection conn) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_VOLATILE_FIRED_TRIGGERS)); setBoolean(ps, 1, true); return ps.executeUpdate(); } finally { closeStatement(ps); } } public int insertSchedulerState(Connection conn, String instanceId, long checkInTime, long interval) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(INSERT_SCHEDULER_STATE)); ps.setString(1, instanceId); ps.setLong(2, checkInTime); ps.setLong(3, interval); return ps.executeUpdate(); } finally { closeStatement(ps); } } public int deleteSchedulerState(Connection conn, String instanceId) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(DELETE_SCHEDULER_STATE)); ps.setString(1, instanceId); return ps.executeUpdate(); } finally { closeStatement(ps); } } public int updateSchedulerState(Connection conn, String instanceId, long checkInTime) throws SQLException { PreparedStatement ps = null; try { ps = conn.prepareStatement(rtp(UPDATE_SCHEDULER_STATE)); ps.setLong(1, checkInTime); ps.setString(2, instanceId); return ps.executeUpdate(); } finally { closeStatement(ps); } } public List selectSchedulerStateRecords(Connection conn, String instanceId) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { List lst = new LinkedList(); if (instanceId != null) { ps = conn.prepareStatement(rtp(SELECT_SCHEDULER_STATE)); ps.setString(1, instanceId); } else { ps = conn.prepareStatement(rtp(SELECT_SCHEDULER_STATES)); } rs = ps.executeQuery(); while (rs.next()) { SchedulerStateRecord rec = new SchedulerStateRecord(); rec.setSchedulerInstanceId(rs.getString(COL_INSTANCE_NAME)); rec.setCheckinTimestamp(rs.getLong(COL_LAST_CHECKIN_TIME)); rec.setCheckinInterval(rs.getLong(COL_CHECKIN_INTERVAL)); lst.add(rec); } return lst; } finally { closeResultSet(rs); closeStatement(ps); } } //--------------------------------------------------------------------------- // protected methods that can be overridden by subclasses //--------------------------------------------------------------------------- /** * <p> * Replace the table prefix in a query by replacing any occurrences of * "{0}" with the table prefix. * </p> * * @param query * the unsubstitued query * @return the query, with proper table prefix substituted */ protected final String rtp(String query) { return Util.rtp(query, tablePrefix); } /** * <p> * Create a serialized <code>java.util.ByteArrayOutputStream</code> * version of an Object. * </p> * * @param obj * the object to serialize * @return the serialized ByteArrayOutputStream * @throws IOException * if serialization causes an error */ protected ByteArrayOutputStream serializeObject(Object obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (null != obj) { ObjectOutputStream out = new ObjectOutputStream(baos); out.writeObject(obj); out.flush(); } return baos; } /** * <p> * Remove the transient data from and then create a serialized <code>java.util.ByteArrayOutputStream</code> * version of a <code>{@link org.quartz.JobDataMap}</code>. * </p> * * @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); } try { return serializeObject(data); } 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 { ByteArrayOutputStream ba = new ByteArrayOutputStream(); if (null != data) { Properties properties = convertToProperty(data.getWrappedMap()); properties.store(ba, ""); } return ba; } /** * convert the JobDataMap into a list of properties */ 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 { Properties properties = new Properties(); 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)) { throw new IOException("JobDataMap values must be Strings " + "when the 'useProperties' property is set. " + " Key of offending value: " + key); } properties.put(key, val); } return properties; } /** * <p> * This method should be overridden by any delegate subclasses that need * special handling for BLOBs. The default implementation uses standard * JDBC <code>java.sql.Blob</code> operations. * </p> * * @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 */ protected Object getObjectFromBlob(ResultSet rs, String colName) throws ClassNotFoundException, IOException, SQLException { Object obj = null; Blob blobLocator = rs.getBlob(colName); if (blobLocator != null && blobLocator.length() != 0) { InputStream binaryInput = blobLocator.getBinaryStream(); if (null != binaryInput) { if (binaryInput instanceof ByteArrayInputStream && ((ByteArrayInputStream) binaryInput).available() == 0) { //do nothing } else { ObjectInputStream in = new ObjectInputStream(binaryInput); try { obj = in.readObject(); } finally { 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)); setBoolean(ps, 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 { closeResultSet(rs); closeStatement(ps); } } public Key[] selectVolatileJobs(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; try { ps = conn.prepareStatement(rtp(SELECT_VOLATILE_JOBS)); setBoolean(ps, 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 { closeResultSet(rs); closeStatement(ps); } } /** * <p> * This method should be overridden by any delegate subclasses that need * special handling for BLOBs for job details. The default implementation * uses standard JDBC <code>java.sql.Blob</code> operations. * </p> * * @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 */ protected Object getJobDetailFromBlob(ResultSet rs, String colName) throws ClassNotFoundException, IOException, SQLException { if (canUseProperties()) { Blob blobLocator = rs.getBlob(colName); if (blobLocator != null) { InputStream binaryInput = blobLocator.getBinaryStream(); return binaryInput; } else { return null; } } return getObjectFromBlob(rs, colName); } /** * @see org.quartz.impl.jdbcjobstore.DriverDelegate#selectPausedTriggerGroups(java.sql.Connection) */ public Set selectPausedTriggerGroups(Connection conn) throws SQLException { PreparedStatement ps = null; ResultSet rs = null; HashSet set = new HashSet(); try { ps = conn.prepareStatement(rtp(SELECT_PAUSED_TRIGGER_GROUPS)); rs = ps.executeQuery(); while (rs.next()) { String groupName = rs.getString(COL_TRIGGER_GROUP); set.add(groupName); } return set; } finally { closeResultSet(rs); closeStatement(ps); } } /** * Cleanup helper method that closes the given <code>ResultSet</code> * while ignoring any errors. */ protected void closeResultSet(ResultSet rs) { if (null != rs) { try { rs.close(); } catch (SQLException ignore) { } } } /** * Cleanup helper method that closes the given <code>Statement</code> * while ignoring any errors. */ protected void closeStatement(Statement statement) { if (null != statement) { try { statement.close(); } catch (SQLException ignore) { } } } /** * Sets the designated parameter to the given Java <code>boolean</code> value. * This just wraps <code>{@link PreparedStatement#setBoolean(int, boolean)}</code> * 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 <code>boolean</code>. * This just wraps <code>{@link ResultSet#getBoolean(java.lang.String)}</code> * 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 <code>boolean</code>. * This just wraps <code>{@link ResultSet#getBoolean(java.lang.String)}</code> * 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 * <code>ByteArrayOutputStream</code>. Will set parameter value to null if the * <code>ByteArrayOutputStream</code> is null. * This just wraps <code>{@link PreparedStatement#setBytes(int, byte[])}</code> * 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