KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > quartz > impl > jdbcjobstore > StdJDBCDelegate


1 /*
2  * Copyright 2004-2005 OpenSymphony
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  *
16  */

17
18 /*
19  * Previously Copyright (c) 2001-2004 James House
20  */

21 package org.quartz.impl.jdbcjobstore;
22
23 import java.io.ByteArrayInputStream JavaDoc;
24 import java.io.ByteArrayOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.NotSerializableException JavaDoc;
28 import java.io.ObjectInputStream JavaDoc;
29 import java.io.ObjectOutputStream JavaDoc;
30 import java.math.BigDecimal JavaDoc;
31 import java.sql.Blob JavaDoc;
32 import java.sql.Connection JavaDoc;
33 import java.sql.PreparedStatement JavaDoc;
34 import java.sql.ResultSet JavaDoc;
35 import java.sql.SQLException JavaDoc;
36 import java.sql.Statement JavaDoc;
37 import java.util.ArrayList JavaDoc;
38 import java.util.Date JavaDoc;
39 import java.util.HashMap JavaDoc;
40 import java.util.HashSet JavaDoc;
41 import java.util.Iterator JavaDoc;
42 import java.util.LinkedList JavaDoc;
43 import java.util.List JavaDoc;
44 import java.util.Map JavaDoc;
45 import java.util.Properties JavaDoc;
46 import java.util.Set JavaDoc;
47 import java.util.TimeZone JavaDoc;
48
49 import org.apache.commons.logging.Log;
50 import org.quartz.Calendar;
51 import org.quartz.CronTrigger;
52 import org.quartz.JobDataMap;
53 import org.quartz.JobDetail;
54 import org.quartz.Scheduler;
55 import org.quartz.SimpleTrigger;
56 import org.quartz.Trigger;
57 import org.quartz.spi.ClassLoadHelper;
58 import org.quartz.utils.Key;
59 import org.quartz.utils.TriggerStatus;
60
61 /**
62  * <p>
63  * This is meant to be an abstract base class for most, if not all, <code>{@link org.quartz.impl.jdbcjobstore.DriverDelegate}</code>
64  * implementations. Subclasses should override only those methods that need
65  * special handling for the DBMS driver in question.
66  * </p>
67  *
68  * @author <a HREF="mailto:jeff@binaryfeed.org">Jeffrey Wescott</a>
69  * @author James House
70  * @author Eric Mueller
71  */

72 public class StdJDBCDelegate implements DriverDelegate, StdJDBCConstants {
73
74     /*
75      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
76      *
77      * Data members.
78      *
79      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
80      */

81
82     protected Log logger = null;
83
84     protected String JavaDoc tablePrefix = DEFAULT_TABLE_PREFIX;
85
86     protected String JavaDoc instanceId;
87
88     protected boolean useProperties;
89
90     /*
91      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
92      *
93      * Constructors.
94      *
95      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
96      */

97
98     /**
99      * <p>
100      * Create new StdJDBCDelegate instance.
101      * </p>
102      *
103      * @param logger
104      * the logger to use during execution
105      * @param tablePrefix
106      * the prefix of all table names
107      */

108     public StdJDBCDelegate(Log logger, String JavaDoc tablePrefix, String JavaDoc instanceId) {
109         this.logger = logger;
110         this.tablePrefix = tablePrefix;
111         this.instanceId = instanceId;
112     }
113
114     /**
115      * <p>
116      * Create new StdJDBCDelegate instance.
117      * </p>
118      *
119      * @param logger
120      * the logger to use during execution
121      * @param tablePrefix
122      * the prefix of all table names
123      */

124     public StdJDBCDelegate(Log logger, String JavaDoc tablePrefix, String JavaDoc instanceId,
125             Boolean JavaDoc useProperties) {
126         this.logger = logger;
127         this.tablePrefix = tablePrefix;
128         this.instanceId = instanceId;
129         this.useProperties = useProperties.booleanValue();
130     }
131
132     /*
133      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
134      *
135      * Interface.
136      *
137      * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
138      */

139
140     protected boolean canUseProperties() {
141         return useProperties;
142     }
143
144     //---------------------------------------------------------------------------
145
// startup / recovery
146
//---------------------------------------------------------------------------
147

148     /**
149      * <p>
150      * Insert the job detail record.
151      * </p>
152      *
153      * @param conn
154      * the DB Connection
155      * @param newState
156      * the new state for the triggers
157      * @param oldState1
158      * the first old state to update
159      * @param oldState2
160      * the second old state to update
161      * @return number of rows updated
162      */

163     public int updateTriggerStatesFromOtherStates(Connection JavaDoc conn,
164             String JavaDoc newState, String JavaDoc oldState1, String JavaDoc oldState2)
165         throws SQLException JavaDoc {
166         PreparedStatement JavaDoc ps = null;
167
168         try {
169             ps = conn
170                     .prepareStatement(rtp(UPDATE_TRIGGER_STATES_FROM_OTHER_STATES));
171             ps.setString(1, newState);
172             ps.setString(2, oldState1);
173             ps.setString(3, oldState2);
174             return ps.executeUpdate();
175         } finally {
176             closeStatement(ps);
177         }
178     }
179
180     /**
181      * <p>
182      * Get the names of all of the triggers that have misfired.
183      * </p>
184      *
185      * @param conn
186      * the DB Connection
187      * @return an array of <code>{@link
188      * org.quartz.utils.Key}</code> objects
189      */

190     public Key[] selectMisfiredTriggers(Connection JavaDoc conn, long ts)
191         throws SQLException JavaDoc {
192         PreparedStatement JavaDoc ps = null;
193         ResultSet JavaDoc rs = null;
194
195         try {
196             ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS));
197             ps.setBigDecimal(1, new BigDecimal JavaDoc(String.valueOf(ts)));
198             rs = ps.executeQuery();
199
200             ArrayList JavaDoc list = new ArrayList JavaDoc();
201             while (rs.next()) {
202                 String JavaDoc triggerName = rs.getString(COL_TRIGGER_NAME);
203                 String JavaDoc groupName = rs.getString(COL_TRIGGER_GROUP);
204                 list.add(new Key(triggerName, groupName));
205             }
206             Object JavaDoc[] oArr = list.toArray();
207             Key[] kArr = new Key[oArr.length];
208             System.arraycopy(oArr, 0, kArr, 0, oArr.length);
209             return kArr;
210         } finally {
211             closeResultSet(rs);
212             closeStatement(ps);
213         }
214     }
215
216     /**
217      * <p>
218      * Select all of the triggers in a given state.
219      * </p>
220      *
221      * @param conn
222      * the DB Connection
223      * @param state
224      * the state the triggers must be in
225      * @return an array of trigger <code>Key</code> s
226      */

227     public Key[] selectTriggersInState(Connection JavaDoc conn, String JavaDoc state)
228         throws SQLException JavaDoc {
229         PreparedStatement JavaDoc ps = null;
230         ResultSet JavaDoc rs = null;
231
232         try {
233             ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_IN_STATE));
234             ps.setString(1, state);
235             rs = ps.executeQuery();
236
237             ArrayList JavaDoc list = new ArrayList JavaDoc();
238             while (rs.next()) {
239                 list.add(new Key(rs.getString(1), rs.getString(2)));
240             }
241
242             Key[] sArr = (Key[]) list.toArray(new Key[list.size()]);
243             return sArr;
244         } finally {
245             closeResultSet(rs);
246             closeStatement(ps);
247         }
248     }
249
250     public Key[] selectMisfiredTriggersInState(Connection JavaDoc conn, String JavaDoc state,
251             long ts) throws SQLException JavaDoc {
252         PreparedStatement JavaDoc ps = null;
253         ResultSet JavaDoc rs = null;
254
255         try {
256             ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS_IN_STATE));
257             ps.setBigDecimal(1, new BigDecimal JavaDoc(String.valueOf(ts)));
258             ps.setString(2, state);
259             rs = ps.executeQuery();
260
261             ArrayList JavaDoc list = new ArrayList JavaDoc();
262             while (rs.next()) {
263                 String JavaDoc triggerName = rs.getString(COL_TRIGGER_NAME);
264                 String JavaDoc groupName = rs.getString(COL_TRIGGER_GROUP);
265                 list.add(new Key(triggerName, groupName));
266             }
267             Object JavaDoc[] oArr = list.toArray();
268             Key[] kArr = new Key[oArr.length];
269             System.arraycopy(oArr, 0, kArr, 0, oArr.length);
270             return kArr;
271         } finally {
272             closeResultSet(rs);
273             closeStatement(ps);
274         }
275     }
276
277     /**
278      * <p>
279      * Get the names of all of the triggers in the given states that have
280      * misfired - according to the given timestamp. No more than count will
281      * be returned.
282      * </p>
283      *
284      * @param conn The DB Connection
285      * @param count The most misfired triggers to return, negative for all
286      * @param resultList Output parameter. A List of
287      * <code>{@link org.quartz.utils.Key}</code> objects. Must not be null.
288      *
289      * @return Whether there are more misfired triggers left to find beyond
290      * the given count.
291      */

292     public boolean selectMisfiredTriggersInStates(Connection JavaDoc conn, String JavaDoc state1, String JavaDoc state2,
293         long ts, int count, List JavaDoc resultList) throws SQLException JavaDoc {
294         PreparedStatement JavaDoc ps = null;
295         ResultSet JavaDoc rs = null;
296
297         try {
298             ps = conn.prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS_IN_STATES));
299             ps.setBigDecimal(1, new BigDecimal JavaDoc(String.valueOf(ts)));
300             ps.setString(2, state1);
301             ps.setString(3, state2);
302             rs = ps.executeQuery();
303
304             boolean hasReachedLimit = false;
305             while (rs.next() && (hasReachedLimit == false)) {
306                 if (resultList.size() == count) {
307                     hasReachedLimit = true;
308                 } else {
309                     String JavaDoc triggerName = rs.getString(COL_TRIGGER_NAME);
310                     String JavaDoc groupName = rs.getString(COL_TRIGGER_GROUP);
311                     resultList.add(new Key(triggerName, groupName));
312                 }
313             }
314             
315             return hasReachedLimit;
316         } finally {
317             closeResultSet(rs);
318             closeStatement(ps);
319         }
320     }
321     
322     /**
323      * <p>
324      * Get the number of triggers in the given states that have
325      * misfired - according to the given timestamp.
326      * </p>
327      *
328      * @param conn the DB Connection
329      */

330     public int countMisfiredTriggersInStates(
331             Connection JavaDoc conn, String JavaDoc state1, String JavaDoc state2, long ts) throws SQLException JavaDoc {
332         PreparedStatement JavaDoc ps = null;
333         ResultSet JavaDoc rs = null;
334
335         try {
336             ps = conn.prepareStatement(rtp(COUNT_MISFIRED_TRIGGERS_IN_STATES));
337             ps.setBigDecimal(1, new BigDecimal JavaDoc(String.valueOf(ts)));
338             ps.setString(2, state1);
339             ps.setString(3, state2);
340             rs = ps.executeQuery();
341
342             if (rs.next()) {
343                 return rs.getInt(1);
344             }
345
346             throw new SQLException JavaDoc("No misfired trigger count returned.");
347         } finally {
348             closeResultSet(rs);
349             closeStatement(ps);
350         }
351     }
352
353     /**
354      * <p>
355      * Get the names of all of the triggers in the given group and state that
356      * have misfired.
357      * </p>
358      *
359      * @param conn
360      * the DB Connection
361      * @return an array of <code>{@link
362      * org.quartz.utils.Key}</code> objects
363      */

364     public Key[] selectMisfiredTriggersInGroupInState(Connection JavaDoc conn,
365             String JavaDoc groupName, String JavaDoc state, long ts) throws SQLException JavaDoc {
366         PreparedStatement JavaDoc ps = null;
367         ResultSet JavaDoc rs = null;
368
369         try {
370             ps = conn
371                     .prepareStatement(rtp(SELECT_MISFIRED_TRIGGERS_IN_GROUP_IN_STATE));
372             ps.setBigDecimal(1, new BigDecimal JavaDoc(String.valueOf(ts)));
373             ps.setString(2, groupName);
374             ps.setString(3, state);
375             rs = ps.executeQuery();
376
377             ArrayList JavaDoc list = new ArrayList JavaDoc();
378             while (rs.next()) {
379                 String JavaDoc triggerName = rs.getString(COL_TRIGGER_NAME);
380                 list.add(new Key(triggerName, groupName));
381             }
382             Object JavaDoc[] oArr = list.toArray();
383             Key[] kArr = new Key[oArr.length];
384             System.arraycopy(oArr, 0, kArr, 0, oArr.length);
385             return kArr;
386         } finally {
387             closeResultSet(rs);
388             closeStatement(ps);
389         }
390     }
391
392     /**
393      * <p>
394      * Select all of the triggers for jobs that are requesting recovery. The
395      * returned trigger objects will have unique "recoverXXX" trigger names and
396      * will be in the <code>{@link
397      * org.quartz.Scheduler}.DEFAULT_RECOVERY_GROUP</code>
398      * trigger group.
399      * </p>
400      *
401      * <p>
402      * In order to preserve the ordering of the triggers, the fire time will be
403      * set from the <code>COL_FIRED_TIME</code> column in the <code>TABLE_FIRED_TRIGGERS</code>
404      * table. The caller is responsible for calling <code>computeFirstFireTime</code>
405      * on each returned trigger. It is also up to the caller to insert the
406      * returned triggers to ensure that they are fired.
407      * </p>
408      *
409      * @param conn
410      * the DB Connection
411      * @return an array of <code>{@link org.quartz.Trigger}</code> objects
412      */

413     public Trigger[] selectTriggersForRecoveringJobs(Connection JavaDoc conn)
414         throws SQLException JavaDoc, IOException JavaDoc, ClassNotFoundException JavaDoc {
415         PreparedStatement JavaDoc ps = null;
416         ResultSet JavaDoc rs = null;
417
418         try {
419             ps = conn
420                     .prepareStatement(rtp(SELECT_INSTANCES_RECOVERABLE_FIRED_TRIGGERS));
421             ps.setString(1, instanceId);
422             setBoolean(ps, 2, true);
423             rs = ps.executeQuery();
424
425             long dumId = System.currentTimeMillis();
426             ArrayList JavaDoc list = new ArrayList JavaDoc();
427             while (rs.next()) {
428                 String JavaDoc jobName = rs.getString(COL_JOB_NAME);
429                 String JavaDoc jobGroup = rs.getString(COL_JOB_GROUP);
430                 String JavaDoc trigName = rs.getString(COL_TRIGGER_NAME);
431                 String JavaDoc trigGroup = rs.getString(COL_TRIGGER_GROUP);
432                 long firedTime = rs.getLong(COL_FIRED_TIME);
433                 int priority = rs.getInt(COL_PRIORITY);
434                 SimpleTrigger rcvryTrig = new SimpleTrigger("recover_"
435                         + instanceId + "_" + String.valueOf(dumId++),
436                         Scheduler.DEFAULT_RECOVERY_GROUP, new Date JavaDoc(firedTime));
437                 rcvryTrig.setJobName(jobName);
438                 rcvryTrig.setJobGroup(jobGroup);
439                 rcvryTrig.setPriority(priority);
440                 rcvryTrig
441                         .setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW);
442
443                 JobDataMap jd = selectTriggerJobDataMap(conn, trigName, trigGroup);
444                 jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_NAME, trigName);
445                 jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_GROUP, trigGroup);
446                 jd.put(Scheduler.FAILED_JOB_ORIGINAL_TRIGGER_FIRETIME_IN_MILLISECONDS, String.valueOf(firedTime));
447                 rcvryTrig.setJobDataMap(jd);
448                 
449                 list.add(rcvryTrig);
450             }
451             Object JavaDoc[] oArr = list.toArray();
452             Trigger[] tArr = new Trigger[oArr.length];
453             System.arraycopy(oArr, 0, tArr, 0, oArr.length);
454             return tArr;
455         } finally {
456             closeResultSet(rs);
457             closeStatement(ps);
458         }
459     }
460
461     /**
462      * <p>
463      * Delete all fired triggers.
464      * </p>
465      *
466      * @param conn
467      * the DB Connection
468      * @return the number of rows deleted
469      */

470     public int deleteFiredTriggers(Connection JavaDoc conn) throws SQLException JavaDoc {
471         PreparedStatement JavaDoc ps = null;
472
473         try {
474             ps = conn.prepareStatement(rtp(DELETE_FIRED_TRIGGERS));
475
476             return ps.executeUpdate();
477         } finally {
478             closeStatement(ps);
479         }
480     }
481
482     public int deleteFiredTriggers(Connection JavaDoc conn, String JavaDoc instanceId)
483         throws SQLException JavaDoc {
484         PreparedStatement JavaDoc ps = null;
485
486         try {
487             ps = conn.prepareStatement(rtp(DELETE_INSTANCES_FIRED_TRIGGERS));
488             ps.setString(1, instanceId);
489
490             return ps.executeUpdate();
491         } finally {
492             closeStatement(ps);
493         }
494     }
495
496     //---------------------------------------------------------------------------
497
// jobs
498
//---------------------------------------------------------------------------
499

500     /**
501      * <p>
502      * Insert the job detail record.
503      * </p>
504      *
505      * @param conn
506      * the DB Connection
507      * @param job
508      * the job to insert
509      * @return number of rows inserted
510      * @throws IOException
511      * if there were problems serializing the JobDataMap
512      */

513     public int insertJobDetail(Connection JavaDoc conn, JobDetail job)
514         throws IOException JavaDoc, SQLException JavaDoc {
515         ByteArrayOutputStream JavaDoc baos = serializeJobData(job.getJobDataMap());
516
517         PreparedStatement JavaDoc ps = null;
518
519         int insertResult = 0;
520
521         try {
522             ps = conn.prepareStatement(rtp(INSERT_JOB_DETAIL));
523             ps.setString(1, job.getName());
524             ps.setString(2, job.getGroup());
525             ps.setString(3, job.getDescription());
526             ps.setString(4, job.getJobClass().getName());
527             setBoolean(ps, 5, job.isDurable());
528             setBoolean(ps, 6, job.isVolatile());
529             setBoolean(ps, 7, job.isStateful());
530             setBoolean(ps, 8, job.requestsRecovery());
531             setBytes(ps, 9, baos);
532
533             insertResult = ps.executeUpdate();
534         } finally {
535             closeStatement(ps);
536         }
537
538         if (insertResult > 0) {
539             String JavaDoc[] jobListeners = job.getJobListenerNames();
540             for (int i = 0; jobListeners != null && i < jobListeners.length; i++) {
541                 insertJobListener(conn, job, jobListeners[i]);
542             }
543         }
544
545         return insertResult;
546     }
547
548     /**
549      * <p>
550      * Update the job detail record.
551      * </p>
552      *
553      * @param conn
554      * the DB Connection
555      * @param job
556      * the job to update
557      * @return number of rows updated
558      * @throws IOException
559      * if there were problems serializing the JobDataMap
560      */

561     public int updateJobDetail(Connection JavaDoc conn, JobDetail job)
562         throws IOException JavaDoc, SQLException JavaDoc {
563         ByteArrayOutputStream JavaDoc baos = serializeJobData(job.getJobDataMap());
564
565         PreparedStatement JavaDoc ps = null;
566
567         int insertResult = 0;
568
569         try {
570             ps = conn.prepareStatement(rtp(UPDATE_JOB_DETAIL));
571             ps.setString(1, job.getDescription());
572             ps.setString(2, job.getJobClass().getName());
573             setBoolean(ps, 3, job.isDurable());
574             setBoolean(ps, 4, job.isVolatile());
575             setBoolean(ps, 5, job.isStateful());
576             setBoolean(ps, 6, job.requestsRecovery());
577             setBytes(ps, 7, baos);
578             ps.setString(8, job.getName());
579             ps.setString(9, job.getGroup());
580
581             insertResult = ps.executeUpdate();
582         } finally {
583             closeStatement(ps);
584         }
585
586         if (insertResult > 0) {
587             deleteJobListeners(conn, job.getName(), job.getGroup());
588
589             String JavaDoc[] jobListeners = job.getJobListenerNames();
590             for (int i = 0; jobListeners != null && i < jobListeners.length; i++) {
591                 insertJobListener(conn, job, jobListeners[i]);
592             }
593         }
594
595         return insertResult;
596     }
597
598     /**
599      * <p>
600      * Get the names of all of the triggers associated with the given job.
601      * </p>
602      *
603      * @param conn
604      * the DB Connection
605      * @param jobName
606      * the name of the job
607      * @param groupName
608      * the group containing the job
609      * @return an array of <code>{@link
610      * org.quartz.utils.Key}</code> objects
611      */

612     public Key[] selectTriggerNamesForJob(Connection JavaDoc conn, String JavaDoc jobName,
613             String JavaDoc groupName) throws SQLException JavaDoc {
614         PreparedStatement JavaDoc ps = null;
615         ResultSet JavaDoc rs = null;
616
617         try {
618             ps = conn.prepareStatement(rtp(SELECT_TRIGGERS_FOR_JOB));
619             ps.setString(1, jobName);
620             ps.setString(2, groupName);
621             rs = ps.executeQuery();
622
623             ArrayList JavaDoc list = new ArrayList JavaDoc(10);
624             while (rs.next()) {
625                 String JavaDoc trigName = rs.getString(COL_TRIGGER_NAME);
626                 String JavaDoc trigGroup = rs.getString(COL_TRIGGER_GROUP);
627                 list.add(new Key(trigName, trigGroup));
628             }
629             Object JavaDoc[] oArr = list.toArray();
630             Key[] kArr = new Key[oArr.length];
631             System.arraycopy(oArr, 0, kArr, 0, oArr.length);
632             return kArr;
633         } finally {
634             closeResultSet(rs);
635             closeStatement(ps);
636         }
637     }
638
639     /**
640      * <p>
641      * Delete all job listeners for the given job.
642      * </p>
643      *
644      * @param conn
645      * the DB Connection
646      * @param jobName
647      * the name of the job
648      * @param groupName
649      * the group containing the job
650      * @return the number of rows deleted
651      */

652     public int deleteJobListeners(Connection JavaDoc conn, String JavaDoc jobName,
653             String JavaDoc groupName) throws SQLException JavaDoc {
654         PreparedStatement JavaDoc ps = null;
655
656         try {
657             ps = conn.prepareStatement(rtp(DELETE_JOB_LISTENERS));
658             ps.setString(1, jobName);
659             ps.setString(2, groupName);
660             return ps.executeUpdate();
661         } finally {
662             closeStatement(ps);
663         }
664     }
665
666     /**
667      * <p>
668      * Delete the job detail record for the given job.
669      * </p>
670      *
671      * @param conn
672      * the DB Connection
673      * @param jobName
674      * the name of the job
675      * @param groupName
676      * the group containing the job
677      * @return the number of rows deleted
678      */

679     public int deleteJobDetail(Connection JavaDoc conn, String JavaDoc jobName, String JavaDoc groupName)
680         throws SQLException JavaDoc {
681         PreparedStatement JavaDoc ps = null;
682
683         try {
684             if (logger.isDebugEnabled()) {
685                 logger.debug("Deleting job: " + groupName + "." + jobName);
686             }
687             ps = conn.prepareStatement(rtp(DELETE_JOB_DETAIL));
688             ps.setString(1, jobName);
689             ps.setString(2, groupName);
690             return ps.executeUpdate();
691         } finally {
692             closeStatement(ps);
693         }
694     }
695
696     /**
697      * <p>
698      * Check whether or not the given job is stateful.
699      * </p>
700      *
701      * @param conn
702      * the DB Connection
703      * @param jobName
704      * the name of the job
705      * @param groupName
706      * the group containing the job
707      * @return true if the job exists and is stateful, false otherwise
708      */

709     public boolean isJobStateful(Connection JavaDoc conn, String JavaDoc jobName,
710             String JavaDoc groupName) throws SQLException JavaDoc {
711         PreparedStatement JavaDoc ps = null;
712         ResultSet JavaDoc rs = null;
713
714         try {
715             ps = conn.prepareStatement(rtp(SELECT_JOB_STATEFUL));
716             ps.setString(1, jobName);
717             ps.setString(2, groupName);
718             rs = ps.executeQuery();
719             if (!rs.next()) { return false; }
720             return getBoolean(rs, COL_IS_STATEFUL);
721         } finally {
722             closeResultSet(rs);
723             closeStatement(ps);
724         }
725     }
726
727     /**
728      * <p>
729      * Check whether or not the given job exists.
730      * </p>
731      *
732      * @param conn
733      * the DB Connection
734      * @param jobName
735      * the name of the job
736      * @param groupName
737      * the group containing the job
738      * @return true if the job exists, false otherwise
739      */

740     public boolean jobExists(Connection JavaDoc conn, String JavaDoc jobName, String JavaDoc groupName)
741         throws SQLException JavaDoc {
742         PreparedStatement JavaDoc ps = null;
743         ResultSet JavaDoc rs = null;
744
745         try {
746             ps = conn.prepareStatement(rtp(SELECT_JOB_EXISTENCE));
747             ps.setString(1, jobName);
748             ps.setString(2, groupName);
749             rs = ps.executeQuery();
750             if (rs.next()) {
751                 return true;
752             } else {
753                 return false;
754             }
755         } finally {
756             closeResultSet(rs);
757             closeStatement(ps);
758         }
759
760     }
761
762     /**
763      * <p>
764      * Update the job data map for the given job.
765      * </p>
766      *
767      * @param conn
768      * the DB Connection
769      * @param job
770      * the job to update
771      * @return the number of rows updated
772      */

773     public int updateJobData(Connection JavaDoc conn, JobDetail job)
774         throws IOException JavaDoc, SQLException JavaDoc {
775         ByteArrayOutputStream JavaDoc baos = serializeJobData(job.getJobDataMap());
776
777         PreparedStatement JavaDoc ps = null;
778
779         try {
780             ps = conn.prepareStatement(rtp(UPDATE_JOB_DATA));
781             setBytes(ps, 1, baos);
782             ps.setString(2, job.getName());
783             ps.setString(3, job.getGroup());
784
785             return ps.executeUpdate();
786         } finally {
787             closeStatement(ps);
788         }
789     }
790
791     /**
792      * <p>
793      * Associate a listener with a job.
794      * </p>
795      *
796      * @param conn
797      * the DB Connection