1 21 package com.jaspersoft.jasperserver.api.engine.scheduling.quartz; 22 23 import java.text.ParseException ; 24 import java.util.Date ; 25 import java.util.HashSet ; 26 import java.util.Iterator ; 27 import java.util.List ; 28 import java.util.Set ; 29 import java.util.SortedSet ; 30 import java.util.TimeZone ; 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 62 public class ReportJobsQuartzScheduler implements ReportJobsScheduler, InitializingBean { 63 64 protected static final Log log = LogFactory.getLog(ReportJobsQuartzScheduler.class); 65 66 private static final String GROUP = "ReportJobs"; 67 private static final String 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 reportExecutionJobClass; 79 80 private final Set listeners; 81 private final SchedulerListener schedulerListener; 82 private final TriggerListener triggerListener; 83 84 public ReportJobsQuartzScheduler() { 85 listeners = new HashSet (); 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 getReportExecutionJobClass() { 100 return reportExecutionJobClass; 101 } 102 103 public void setReportExecutionJobClass(Class 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 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 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 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 jobName(long jobId) { 187 return "job_" + jobId; 188 } 189 190 protected String 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 (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 triggerName = triggerName(jobTrigger); 216 Date startDate = getStartDate(jobTrigger); 217 Date 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 getEndDate(ReportJobTrigger jobTrigger) { 250 return translateFromTriggerTimeZone(jobTrigger, jobTrigger.getEndDate()); 251 } 252 253 protected Date translateFromTriggerTimeZone(ReportJobTrigger jobTrigger, Date date) { 254 if (date != null) { 255 TimeZone 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 getStartDate(ReportJobTrigger jobTrigger) { 264 Date startDate; 265 switch (jobTrigger.getStartType()) { 266 case ReportJobTrigger.START_TYPE_NOW: 267 startDate = new Date (); 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 triggerName = triggerName(jobTrigger); 294 Date startDate = getStartDate(jobTrigger); 295 Date endDate = getEndDate(jobTrigger); 296 297 String 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 timeZone = getTriggerTimeZone(jobTrigger); 306 if (timeZone != null) { 307 trigger.setTimeZone(timeZone); 308 } 309 310 return trigger; 311 } catch (ParseException e) { 312 log.error("Error creating Quartz Cron trigger", e); 313 throw new JSExceptionWrapper(e); 314 } 315 } 316 317 protected TimeZone getTriggerTimeZone(ReportJobTrigger jobTrigger) { 318 String tzId = jobTrigger.getTimezone(); 319 TimeZone 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 getCronExpression(ReportJobCalendarTrigger jobTrigger) { 332 String minutes = jobTrigger.getMinutes(); 333 String hours = jobTrigger.getHours(); 334 String weekDays; 335 String 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 months = enumerateCronVals(jobTrigger.getMonths(), COUNT_MONTHS); 353 354 StringBuffer cronExpression = new StringBuffer (); 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 enumerateCronVals(SortedSet 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 enumStr = new StringBuffer (); 379 for (Iterator it = vals.iterator(); it.hasNext();) { 380 Byte val = (Byte ) 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 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 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 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 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 getExecutingJobNames() throws SchedulerException { 466 List executingJobs = scheduler.getCurrentlyExecutingJobs(); 467 Set executingJobNames = new HashSet (); 468 for (Iterator 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 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 name, String 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 name, String group) { 532 } 533 534 public void triggersResumed(String name, String group) { 535 } 536 537 public void jobsPaused(String name, String group) { 538 } 539 540 public void jobsResumed(String name, String group) { 541 } 542 543 public void schedulerError(String 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 name; 561 562 public ReportSchedulerTriggerListener(String name) { 563 this.name = name; 564 } 565 566 public String 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 ()) == 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 |