KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jaspersoft > jasperserver > api > engine > scheduling > quartz > ReportJobsQuartzScheduler


1 /*
2  * Copyright (C) 2006 JasperSoft http://www.jaspersoft.com
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed WITHOUT ANY WARRANTY; and without the
10  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  * See the GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, see http://www.gnu.org/licenses/gpl.txt
15  * or write to:
16  *
17  * Free Software Foundation, Inc.,
18  * 59 Temple Place - Suite 330,
19  * Boston, MA USA 02111-1307
20  */

21 package com.jaspersoft.jasperserver.api.engine.scheduling.quartz;
22
23 import java.text.ParseException JavaDoc;
24 import java.util.Date JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Set JavaDoc;
29 import java.util.SortedSet JavaDoc;
30 import java.util.TimeZone JavaDoc;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.quartz.CronTrigger;
35 import org.quartz.JobDataMap;
36 import org.quartz.JobDetail;
37 import org.quartz.JobExecutionContext;
38 import org.quartz.Scheduler;
39 import org.quartz.SchedulerException;
40 import org.quartz.SchedulerListener;
41 import org.quartz.SimpleTrigger;
42 import org.quartz.Trigger;
43 import org.quartz.TriggerListener;
44 import org.quartz.TriggerUtils;
45 import org.springframework.beans.factory.InitializingBean;
46
47 import com.jaspersoft.jasperserver.api.JSException;
48 import com.jaspersoft.jasperserver.api.JSExceptionWrapper;
49 import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
50 import com.jaspersoft.jasperserver.api.engine.scheduling.domain.ReportJob;
51 import com.jaspersoft.jasperserver.api.engine.scheduling.domain.ReportJobCalendarTrigger;
52 import com.jaspersoft.jasperserver.api.engine.scheduling.domain.ReportJobRuntimeInformation;
53 import com.jaspersoft.jasperserver.api.engine.scheduling.domain.ReportJobSimpleTrigger;
54 import com.jaspersoft.jasperserver.api.engine.scheduling.domain.ReportJobTrigger;
55 import com.jaspersoft.jasperserver.api.engine.scheduling.service.ReportJobsScheduler;
56 import com.jaspersoft.jasperserver.api.engine.scheduling.service.ReportSchedulerListener;
57
58 /**
59  * @author Lucian Chirita (lucianc@users.sourceforge.net)
60  * @version $Id: ReportJobsQuartzScheduler.java 3834 2006-06-26 06:41:17Z lucian $
61  */

62 public class ReportJobsQuartzScheduler implements ReportJobsScheduler, InitializingBean {
63
64     protected static final Log log = LogFactory.getLog(ReportJobsQuartzScheduler.class);
65
66     private static final String JavaDoc GROUP = "ReportJobs";
67     private static final String JavaDoc TRIGGER_LISTENER_NAME = "reportSchedulerTriggerListener";
68     
69     private static final long COEFFICIENT_MINUTE = 60l * 1000l;
70     private static final long COEFFICIENT_HOUR = 60l * COEFFICIENT_MINUTE;
71     private static final long COEFFICIENT_DAY = 24l * COEFFICIENT_HOUR;
72     private static final long COEFFICIENT_WEEK = 7l * COEFFICIENT_DAY;
73     
74     private static final int COUNT_WEEKDAYS = 7;
75     private static final int COUNT_MONTHS = 12;
76     
77     private Scheduler scheduler;
78     private Class JavaDoc reportExecutionJobClass;
79     
80     private final Set JavaDoc listeners;
81     private final SchedulerListener schedulerListener;
82     private final TriggerListener triggerListener;
83     
84     public ReportJobsQuartzScheduler() {
85         listeners = new HashSet JavaDoc();
86
87         schedulerListener = new ReportSchedulerQuartzListener();
88         triggerListener = new ReportSchedulerTriggerListener(TRIGGER_LISTENER_NAME);
89     }
90
91     public Scheduler getScheduler() {
92         return scheduler;
93     }
94
95     public void setScheduler(Scheduler scheduler) {
96         this.scheduler = scheduler;
97     }
98
99     public Class JavaDoc getReportExecutionJobClass() {
100         return reportExecutionJobClass;
101     }
102
103     public void setReportExecutionJobClass(Class JavaDoc reportExecutionJobClass) {
104         this.reportExecutionJobClass = reportExecutionJobClass;
105     }
106
107
108     public void afterPropertiesSet() {
109         try {
110             getScheduler().addSchedulerListener(schedulerListener);
111             getScheduler().addTriggerListener(triggerListener);
112         } catch (SchedulerException e) {
113             log.error("Error (de)registering Quartz listener", e);
114             throw new JSExceptionWrapper(e);
115         }
116     }
117     
118     public void scheduleJob(ExecutionContext context, ReportJob job) {
119         String JavaDoc jobName = jobName(job.getId());
120         JobDetail jobDetail = new JobDetail(jobName, GROUP, getReportExecutionJobClass(), false, false, false);
121         
122         Trigger trigger = createTrigger(job);
123         try {
124             scheduler.scheduleJob(jobDetail, trigger);
125             
126             if (log.isDebugEnabled()) {
127                 log.debug("Created job " + jobDetail.getFullName() + " and trigger " + trigger.getFullName() + " for job " + job.getId());
128             }
129         } catch (SchedulerException e) {
130             log.error("Error scheduling Quartz job", e);
131             throw new JSExceptionWrapper(e);
132         }
133     }
134
135
136     public void rescheduleJob(ExecutionContext context, ReportJob job) {
137         try {
138             Trigger oldTrigger = getReportJobTrigger(job.getId());
139             
140             String JavaDoc jobName = jobName(job.getId());
141             Trigger trigger = createTrigger(job);
142             trigger.setJobName(jobName);
143             trigger.setJobGroup(GROUP);
144             
145             if (oldTrigger == null) {
146                 scheduler.scheduleJob(trigger);
147                 
148                 if (log.isDebugEnabled()) {
149                     log.debug("Scheduled trigger " + trigger.getFullName() + " for job " + job.getId());
150                 }
151             } else {
152                 scheduler.rescheduleJob(oldTrigger.getName(), oldTrigger.getGroup(), trigger);
153
154                 if (log.isDebugEnabled()) {
155                     log.debug("Trigger " + oldTrigger.getFullName() + " rescheduled by " + trigger.getFullName() + " for job " + job.getId());
156                 }
157             }
158         } catch (SchedulerException e) {
159             log.error("Error rescheduling Quartz job", e);
160             throw new JSExceptionWrapper(e);
161         }
162     }
163     
164     protected Trigger getReportJobTrigger(long jobId) throws SchedulerException {
165         Trigger trigger;
166         String JavaDoc jobName = jobName(jobId);
167         Trigger[] triggers = scheduler.getTriggersOfJob(jobName, GROUP);
168         if (triggers == null || triggers.length == 0) {
169             trigger = null;
170             
171             if (log.isDebugEnabled()) {
172                 log.debug("No trigger found for job " + jobId);
173             }
174         } else if (triggers.length == 1) {
175             trigger = triggers[0];
176
177             if (log.isDebugEnabled()) {
178                 log.debug("Trigger " + trigger.getFullName() + " found for job " + jobId);
179             }
180         } else {
181             throw new JSException("Job " + jobId + " has more than one trigger");
182         }
183         return trigger;
184     }
185
186     protected String JavaDoc jobName(long jobId) {
187         return "job_" + jobId;
188     }
189
190     protected String JavaDoc triggerName(ReportJobTrigger jobTrigger) {
191         return "trigger_" + jobTrigger.getId() + "_" + jobTrigger.getVersion();
192     }
193
194     protected Trigger createTrigger(ReportJob reportJob) {
195         Trigger trigger;
196         ReportJobTrigger jobTrigger = reportJob.getTrigger();
197         if (jobTrigger instanceof ReportJobSimpleTrigger) {
198             trigger = createTrigger((ReportJobSimpleTrigger) jobTrigger);
199         } else if (jobTrigger instanceof ReportJobCalendarTrigger) {
200             trigger = createTrigger((ReportJobCalendarTrigger) jobTrigger);
201         } else {
202             throw new JSException("Unknow report job trigger type \"" + jobTrigger.getClass().getName() + "\"");
203         }
204         
205         JobDataMap jobDataMap = trigger.getJobDataMap();
206         jobDataMap.put(ReportExecutionJob.JOB_DATA_KEY_DETAILS_ID, new Long JavaDoc(reportJob.getId()));
207         jobDataMap.put(ReportExecutionJob.JOB_DATA_KEY_USERNAME, reportJob.getUsername());
208         
209         trigger.addTriggerListener(TRIGGER_LISTENER_NAME);
210         
211         return trigger;
212     }
213     
214     protected Trigger createTrigger(ReportJobSimpleTrigger jobTrigger) {
215         String JavaDoc triggerName = triggerName(jobTrigger);
216         Date JavaDoc startDate = getStartDate(jobTrigger);
217         Date JavaDoc endDate = getEndDate(jobTrigger);
218             
219         int repeatCount = repeatCount(jobTrigger);
220         SimpleTrigger trigger;
221         if (repeatCount == 0) {
222             trigger = new SimpleTrigger(triggerName, GROUP, startDate);
223         } else {
224             int recurrenceInterval = jobTrigger.getRecurrenceInterval().intValue();
225
226             switch (jobTrigger.getRecurrenceIntervalUnit().byteValue()) {
227             case ReportJobSimpleTrigger.INTERVAL_MINUTE:
228                 trigger = new SimpleTrigger(triggerName, GROUP, startDate, endDate, repeatCount, recurrenceInterval * COEFFICIENT_MINUTE);
229                 break;
230             case ReportJobSimpleTrigger.INTERVAL_HOUR:
231                 trigger = new SimpleTrigger(triggerName, GROUP, startDate, endDate, repeatCount, recurrenceInterval * COEFFICIENT_HOUR);
232                 break;
233             case ReportJobSimpleTrigger.INTERVAL_DAY:
234                 trigger = new SimpleTrigger(triggerName, GROUP, startDate, endDate, repeatCount, recurrenceInterval * COEFFICIENT_DAY);
235                 break;
236             case ReportJobSimpleTrigger.INTERVAL_WEEK:
237                 trigger = new SimpleTrigger(triggerName, GROUP, startDate, endDate, repeatCount, recurrenceInterval * COEFFICIENT_WEEK);
238                 break;
239             default:
240                 throw new JSException("Unknown report job interval unit " + jobTrigger.getRecurrenceIntervalUnit());
241             }
242         }
243         
244         trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);
245
246         return trigger;
247     }
248
249     protected Date JavaDoc getEndDate(ReportJobTrigger jobTrigger) {
250         return translateFromTriggerTimeZone(jobTrigger, jobTrigger.getEndDate());
251     }
252     
253     protected Date JavaDoc translateFromTriggerTimeZone(ReportJobTrigger jobTrigger, Date JavaDoc date) {
254         if (date != null) {
255             TimeZone JavaDoc tz = getTriggerTimeZone(jobTrigger);
256             if (tz != null) {
257                 date = TriggerUtils.translateTime(date, TimeZone.getDefault(), tz);
258             }
259         }
260         return date;
261     }
262
263     protected Date JavaDoc getStartDate(ReportJobTrigger jobTrigger) {
264         Date JavaDoc startDate;
265         switch (jobTrigger.getStartType()) {
266         case ReportJobTrigger.START_TYPE_NOW:
267             startDate = new Date JavaDoc();
268             break;
269         case ReportJobTrigger.START_TYPE_SCHEDULE:
270             startDate = translateFromTriggerTimeZone(jobTrigger, jobTrigger.getStartDate());
271             break;
272         default:
273             throw new JSException("Unknown report job start type " + jobTrigger.getStartType());
274         }
275         return startDate;
276     }
277
278     protected int repeatCount(ReportJobSimpleTrigger jobTrigger) {
279         int recurrenceCount = jobTrigger.getOccurrenceCount();
280         int repeatCount;
281         switch (recurrenceCount) {
282         case ReportJobSimpleTrigger.RECUR_INDEFINITELY:
283             repeatCount = SimpleTrigger.REPEAT_INDEFINITELY;
284             break;
285         default:
286             repeatCount = recurrenceCount - 1;
287             break;
288         }
289         return repeatCount;
290     }
291     
292     protected Trigger createTrigger(ReportJobCalendarTrigger jobTrigger) {
293         String JavaDoc triggerName = triggerName(jobTrigger);
294         Date JavaDoc startDate = getStartDate(jobTrigger);
295         Date JavaDoc endDate = getEndDate(jobTrigger);
296         
297         String JavaDoc cronExpression = getCronExpression(jobTrigger);
298         
299         try {
300             CronTrigger trigger = new CronTrigger(triggerName, GROUP, cronExpression);
301             trigger.setStartTime(startDate);
302             trigger.setEndTime(endDate);
303             trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
304             
305             TimeZone JavaDoc timeZone = getTriggerTimeZone(jobTrigger);
306             if (timeZone != null) {
307                 trigger.setTimeZone(timeZone);
308             }
309             
310             return trigger;
311         } catch (ParseException JavaDoc e) {
312             log.error("Error creating Quartz Cron trigger", e);
313             throw new JSExceptionWrapper(e);
314         }
315     }
316
317     protected TimeZone JavaDoc getTriggerTimeZone(ReportJobTrigger jobTrigger) {
318         String JavaDoc tzId = jobTrigger.getTimezone();
319         TimeZone JavaDoc tz;
320         if (tzId == null || tzId.length() == 0) {
321             tz = null;
322         } else {
323             tz = TimeZone.getTimeZone(tzId);
324             if (tz == null) {
325                 throw new JSException("Unknow time zone \"" + tzId + "\"");
326             }
327         }
328         return tz;
329     }
330
331     protected String JavaDoc getCronExpression(ReportJobCalendarTrigger jobTrigger) {
332         String JavaDoc minutes = jobTrigger.getMinutes();
333         String JavaDoc hours = jobTrigger.getHours();
334         String JavaDoc weekDays;
335         String JavaDoc monthDays;
336         switch (jobTrigger.getDaysType()) {
337         case ReportJobCalendarTrigger.DAYS_TYPE_ALL:
338             weekDays = "?";
339             monthDays = "*";
340             break;
341         case ReportJobCalendarTrigger.DAYS_TYPE_WEEK:
342             weekDays = enumerateCronVals(jobTrigger.getWeekDays(), COUNT_WEEKDAYS);
343             monthDays = "?";
344             break;
345         case ReportJobCalendarTrigger.DAYS_TYPE_MONTH:
346             weekDays = "?";
347             monthDays = jobTrigger.getMonthDays();
348             break;
349         default:
350             throw new JSException("Unknow calendar trigger days type " + jobTrigger.getDaysType());
351         }
352         String JavaDoc months = enumerateCronVals(jobTrigger.getMonths(), COUNT_MONTHS);
353         
354         StringBuffer JavaDoc cronExpression = new StringBuffer JavaDoc();
355         cronExpression.append("0 ");
356         cronExpression.append(minutes);
357         cronExpression.append(' ');
358         cronExpression.append(hours);
359         cronExpression.append(' ');
360         cronExpression.append(monthDays);
361         cronExpression.append(' ');
362         cronExpression.append(months);
363         cronExpression.append(' ');
364         cronExpression.append(weekDays);
365         
366         return cronExpression.toString();
367     }
368
369     protected String JavaDoc enumerateCronVals(SortedSet JavaDoc vals, int totalCount) {
370         if (vals == null || vals.isEmpty()) {
371             throw new JSException("No values to enumerate");
372         }
373         
374         if (vals.size() == totalCount) {
375             return "*";
376         }
377         
378         StringBuffer JavaDoc enumStr = new StringBuffer JavaDoc();
379         for (Iterator JavaDoc it = vals.iterator(); it.hasNext();) {
380             Byte JavaDoc val = (Byte JavaDoc) it.next();
381             enumStr.append(val.byteValue());
382             enumStr.append(',');
383         }
384         return enumStr.substring(0, enumStr.length() - 1);
385     }
386
387     public void removeScheduledJob(ExecutionContext context, long jobId) {
388         try {
389             String JavaDoc jobName = jobName(jobId);
390             if (scheduler.deleteJob(jobName, GROUP)) {
391                 if (log.isDebugEnabled()) {
392                     log.debug("Job " + jobName + "deleted");
393                 }
394             } else {
395                 log.info("Quartz job " + jobId + " was not found to be deleted");
396             }
397         } catch (SchedulerException e) {
398             log.error("Error deleting Quartz job " + jobId, e);
399             throw new JSExceptionWrapper(e);
400         }
401     }
402
403
404     public ReportJobRuntimeInformation[] getJobsRuntimeInformation(ExecutionContext context, long[] jobIds) {
405         if (jobIds == null) {
406             return null;
407         }
408         
409         try {
410             Set JavaDoc executingJobNames = getExecutingJobNames();
411             ReportJobRuntimeInformation[] infos = new ReportJobRuntimeInformation[jobIds.length];
412             for (int i = 0; i < jobIds.length; i++) {
413                 infos[i] = getJobRuntimeInformation(jobIds[i], executingJobNames);
414             }
415             return infos;
416         } catch (SchedulerException e) {
417             log.error("Error while fetching Quartz runtime information", e);
418             throw new JSExceptionWrapper(e);
419         }
420     }
421
422     protected ReportJobRuntimeInformation getJobRuntimeInformation(long jobId, Set JavaDoc executingJobNames) throws SchedulerException {
423         ReportJobRuntimeInformation info = new ReportJobRuntimeInformation();
424         Trigger trigger = getReportJobTrigger(jobId);
425         if (trigger == null) {
426             info.setState(ReportJobRuntimeInformation.STATE_UNKNOWN);
427         } else {
428             info.setPreviousFireTime(trigger.getPreviousFireTime());
429             if (trigger.mayFireAgain()) {
430                 info.setNextFireTime(trigger.getNextFireTime());
431             }
432             
433             byte state = getJobState(trigger, executingJobNames);
434             info.setState(state);
435         }
436         return info;
437     }
438
439     protected byte getJobState(Trigger trigger, Set JavaDoc executingJobNames) throws SchedulerException {
440         byte state;
441         int quartzState = scheduler.getTriggerState(trigger.getName(), trigger.getGroup());
442         switch (quartzState) {
443         case Trigger.STATE_NORMAL:
444         case Trigger.STATE_BLOCKED:
445             state = executingJobNames.contains(trigger.getJobName()) ?
446                     ReportJobRuntimeInformation.STATE_EXECUTING :
447                         ReportJobRuntimeInformation.STATE_NORMAL;
448             break;
449         case Trigger.STATE_COMPLETE:
450             state = ReportJobRuntimeInformation.STATE_COMPLETE;
451             break;
452         case Trigger.STATE_PAUSED:
453             state = ReportJobRuntimeInformation.STATE_PAUSED;
454             break;
455         case Trigger.STATE_ERROR:
456             state = ReportJobRuntimeInformation.STATE_ERROR;
457             break;
458         default:
459             state = ReportJobRuntimeInformation.STATE_UNKNOWN;
460             break;
461         }
462         return state;
463     }
464
465     protected Set JavaDoc getExecutingJobNames() throws SchedulerException {
466         List JavaDoc executingJobs = scheduler.getCurrentlyExecutingJobs();
467         Set JavaDoc executingJobNames = new HashSet JavaDoc();
468         for (Iterator JavaDoc iter = executingJobs.iterator(); iter.hasNext();) {
469             JobExecutionContext executionContext = (JobExecutionContext) iter.next();
470             JobDetail jobDetail = executionContext.getJobDetail();
471             if (jobDetail.getGroup().equals(GROUP)) {
472                 executingJobNames.add(jobDetail.getName());
473             }
474         }
475         return executingJobNames;
476     }
477
478     public void addReportSchedulerListener(ReportSchedulerListener listener) {
479         synchronized (listeners) {
480             listeners.add(listener);
481         }
482     }
483
484     public synchronized void removeReportSchedulerListener(ReportSchedulerListener listener) {
485         synchronized (listeners) {
486             listeners.remove(listener);
487         }
488     }
489
490     protected void notifyListenersOfFinalizedJob(long jobId) {
491         synchronized (listeners) {
492             for (Iterator JavaDoc it = listeners.iterator(); it.hasNext();) {
493                 ReportSchedulerListener listener = (ReportSchedulerListener) it.next();
494                 listener.reportJobFinalized(jobId);
495             }
496         }
497     }
498
499     protected void reportTriggerFinalized(Trigger trigger) {
500         long jobId = trigger.getJobDataMap().getLong(ReportExecutionJob.JOB_DATA_KEY_DETAILS_ID);
501         notifyListenersOfFinalizedJob(jobId);
502     }
503
504     protected class ReportSchedulerQuartzListener implements SchedulerListener {
505         
506         public ReportSchedulerQuartzListener() {
507         }
508
509         public void jobScheduled(Trigger trigger) {
510             if (log.isDebugEnabled()) {
511                 log.debug("Quartz job " + trigger.getFullJobName() + " scheduled by trigger " + trigger.getFullName());
512             }
513         }
514
515         public void jobUnscheduled(String JavaDoc name, String JavaDoc group) {
516             if (log.isDebugEnabled()) {
517                 log.debug("Quartz job unscheduled " + group + "." + name);
518             }
519         }
520
521         public void triggerFinalized(Trigger trigger) {
522             if (log.isDebugEnabled()) {
523                 log.debug("Quartz trigger finalized " + trigger.getFullName());
524             }
525             
526             if (trigger.getGroup().equals(GROUP)) {
527                 reportTriggerFinalized(trigger);
528             }
529         }
530
531         public void triggersPaused(String JavaDoc name, String JavaDoc group) {
532         }
533
534         public void triggersResumed(String JavaDoc name, String JavaDoc group) {
535         }
536
537         public void jobsPaused(String JavaDoc name, String JavaDoc group) {
538         }
539
540         public void jobsResumed(String JavaDoc name, String JavaDoc group) {
541         }
542
543         public void schedulerError(String JavaDoc msg, SchedulerException cause) {
544             if (log.isInfoEnabled()) {
545                 log.info("Quartz scheduler error: " + msg, cause);
546             }
547         }
548
549         public void schedulerShutdown() {
550             if (log.isInfoEnabled()) {
551                 log.info("Quartz scheduler shutdown");
552             }
553         }
554         
555     }
556     
557     
558     protected class ReportSchedulerTriggerListener implements TriggerListener {
559
560         private final String JavaDoc name;
561
562         public ReportSchedulerTriggerListener(String JavaDoc name) {
563             this.name = name;
564         }
565         
566         public String JavaDoc getName() {
567             return name;
568         }
569
570         public void triggerFired(Trigger trigger, JobExecutionContext context) {
571             if (log.isDebugEnabled()) {
572                 log.debug("Quartz trigger fired " + trigger.getFullName());
573             }
574         }
575
576         public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
577             return false;
578         }
579
580         public void triggerMisfired(Trigger trigger) {
581             if (log.isDebugEnabled()) {
582                 log.debug("Quartz trigger misfired " + trigger.getFullName());
583             }
584             
585             if (trigger.getGroup().equals(GROUP) && trigger.getFireTimeAfter(new Date JavaDoc()) == null) {
586                 reportTriggerFinalized(trigger);
587             }
588         }
589
590         public void triggerComplete(Trigger trigger, JobExecutionContext context, int triggerInstructionCode) {
591             if (log.isDebugEnabled()) {
592                 log.debug("Quartz trigger complete " + trigger.getFullName() + " " + triggerInstructionCode);
593             }
594         }
595
596     }
597
598 }
599
Popular Tags