1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.ole.sys.batch.service.impl;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Calendar;
21 import java.util.Collections;
22 import java.util.Date;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.Set;
28
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.log4j.Logger;
31 import org.kuali.ole.sys.OLEConstants;
32 import org.kuali.ole.sys.batch.BatchJobStatus;
33 import org.kuali.ole.sys.batch.BatchSpringContext;
34 import org.kuali.ole.sys.batch.Job;
35 import org.kuali.ole.sys.batch.JobDescriptor;
36 import org.kuali.ole.sys.batch.JobListener;
37 import org.kuali.ole.sys.batch.ScheduleStep;
38 import org.kuali.ole.sys.batch.SimpleTriggerDescriptor;
39 import org.kuali.ole.sys.batch.Step;
40 import org.kuali.ole.sys.batch.service.SchedulerService;
41 import org.kuali.ole.sys.context.SpringContext;
42 import org.kuali.ole.sys.service.BatchModuleService;
43 import org.kuali.ole.sys.service.impl.OleModuleServiceImpl;
44 import org.kuali.rice.core.api.datetime.DateTimeService;
45 import org.kuali.rice.coreservice.framework.parameter.ParameterService;
46 import org.kuali.rice.krad.service.KualiModuleService;
47 import org.kuali.rice.krad.service.MailService;
48 import org.kuali.rice.krad.service.ModuleService;
49 import org.quartz.JobDetail;
50 import org.quartz.JobExecutionContext;
51 import org.quartz.ObjectAlreadyExistsException;
52 import org.quartz.Scheduler;
53 import org.quartz.SchedulerException;
54 import org.quartz.Trigger;
55 import org.quartz.UnableToInterruptJobException;
56 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
57 import org.springframework.transaction.annotation.Transactional;
58
59 @Transactional
60 public class SchedulerServiceImpl implements SchedulerService {
61 private static final Logger LOG = Logger.getLogger(SchedulerServiceImpl.class);
62 protected static final String SOFT_DEPENDENCY_CODE = "softDependency";
63 protected static final String HARD_DEPENDENCY_CODE = "hardDependency";
64
65 protected Scheduler scheduler;
66 protected JobListener jobListener;
67 private KualiModuleService kualiModuleService;
68 protected ParameterService parameterService;
69 protected DateTimeService dateTimeService;
70 private MailService mailService;
71
72
73
74 protected Map<String, JobDescriptor> externalizedJobDescriptors;
75
76 protected static final List<String> jobStatuses = new ArrayList<String>();
77
78 static {
79 jobStatuses.add(SCHEDULED_JOB_STATUS_CODE);
80 jobStatuses.add(SUCCEEDED_JOB_STATUS_CODE);
81 jobStatuses.add(CANCELLED_JOB_STATUS_CODE);
82 jobStatuses.add(RUNNING_JOB_STATUS_CODE);
83 jobStatuses.add(FAILED_JOB_STATUS_CODE);
84 }
85
86 public SchedulerServiceImpl() {
87 externalizedJobDescriptors = new HashMap<String, JobDescriptor>();
88 }
89
90
91
92
93 @Override
94 public void initialize() {
95 LOG.info("Initializing the schedule");
96 jobListener.setSchedulerService(this);
97 try {
98 scheduler.addGlobalJobListener(jobListener);
99 }
100 catch (SchedulerException e) {
101 throw new RuntimeException("SchedulerServiceImpl encountered an exception when trying to register the global job listener", e);
102 }
103 JobDescriptor jobDescriptor;
104 for (ModuleService moduleService : kualiModuleService.getInstalledModuleServices()) {
105 initializeJobsForModule(moduleService);
106 initializeTriggersForModule(moduleService);
107 }
108
109 dropDependenciesNotScheduled();
110 }
111
112
113
114
115
116 protected void initializeJobsForModule(ModuleService moduleService) {
117 if ( LOG.isInfoEnabled() ) {
118 LOG.info("Loading scheduled jobs for: " + moduleService.getModuleConfiguration().getNamespaceCode());
119 }
120 JobDescriptor jobDescriptor;
121 if ( moduleService.getModuleConfiguration().getJobNames() != null ) {
122 for (String jobName : moduleService.getModuleConfiguration().getJobNames()) {
123 try {
124 if (moduleService instanceof BatchModuleService && ((BatchModuleService) moduleService).isExternalJob(jobName)) {
125 jobDescriptor = new JobDescriptor();
126 jobDescriptor.setBeanName(jobName);
127 jobDescriptor.setGroup(SCHEDULED_GROUP);
128 jobDescriptor.setDurable(false);
129 externalizedJobDescriptors.put(jobName, jobDescriptor);
130 }
131 else {
132 jobDescriptor = BatchSpringContext.getJobDescriptor(jobName);
133 }
134 jobDescriptor.setNamespaceCode(moduleService.getModuleConfiguration().getNamespaceCode());
135 loadJob(jobDescriptor);
136 } catch (NoSuchBeanDefinitionException ex) {
137 LOG.error("unable to find job bean definition for job: " + ex.getBeanName());
138 } catch ( Exception ex ) {
139 LOG.error( "Unable to install " + jobName + " job into scheduler.", ex );
140 }
141 }
142 }
143 }
144
145
146
147
148
149 protected void initializeTriggersForModule(ModuleService moduleService) {
150 if ( moduleService.getModuleConfiguration().getTriggerNames() != null ) {
151 for (String triggerName : moduleService.getModuleConfiguration().getTriggerNames()) {
152 try {
153 addTrigger(BatchSpringContext.getTriggerDescriptor(triggerName).getTrigger());
154 } catch (NoSuchBeanDefinitionException ex) {
155 LOG.error("unable to find trigger definition: " + ex.getBeanName());
156 } catch ( Exception ex ) {
157 LOG.error( "Unable to install " + triggerName + " trigger into scheduler.", ex );
158 }
159 }
160 }
161 }
162
163
164 protected void loadJob(JobDescriptor jobDescriptor) {
165 JobDetail jobDetail = jobDescriptor.getJobDetail();
166 addJob(jobDetail);
167 if (SCHEDULED_GROUP.equals(jobDetail.getGroup())) {
168 jobDetail.setGroup(UNSCHEDULED_GROUP);
169 addJob(jobDetail);
170 }
171 }
172
173
174
175
176
177 protected void dropDependenciesNotScheduled() {
178 try {
179 List<String> scheduledGroupJobNames = Arrays.asList(scheduler.getJobNames(SCHEDULED_GROUP));
180
181 for (String jobName : scheduledGroupJobNames) {
182 JobDescriptor jobDescriptor = BatchSpringContext.getJobDescriptor(jobName);
183
184 if (jobDescriptor != null && jobDescriptor.getDependencies() != null) {
185
186 ArrayList<Entry<String, String>> dependenciesToBeRemoved = new ArrayList<Entry<String, String>>();
187 Set<Entry<String, String>> dependenciesSet = jobDescriptor.getDependencies().entrySet();
188 for (Entry<String, String> dependency : dependenciesSet) {
189 String dependencyJobName = dependency.getKey();
190 if (!scheduledGroupJobNames.contains(dependencyJobName)) {
191 LOG.warn("Removing dependency " + dependencyJobName + " from " + jobName + " because it is not scheduled.");
192 dependenciesToBeRemoved.add(dependency);
193 }
194 }
195 dependenciesSet.removeAll(dependenciesToBeRemoved);
196 }
197 }
198 } catch (SchedulerException e) {
199 throw new RuntimeException("Caught exception while trying to drop dependencies that are not scheduled", e);
200 }
201 }
202
203
204
205
206 @Override
207 public void initializeJob(String jobName, Job job) {
208 job.setSchedulerService(this);
209 job.setParameterService(parameterService);
210 job.setSteps(BatchSpringContext.getJobDescriptor(jobName).getSteps());
211 job.setDateTimeService(dateTimeService);
212 }
213
214
215
216
217 @Override
218 public boolean hasIncompleteJob() {
219 StringBuilder log = new StringBuilder("The schedule has incomplete jobs.");
220 boolean hasIncompleteJob = false;
221 for (String scheduledJobName : getJobNamesForScheduleJob() ) {
222 JobDetail scheduledJobDetail = getScheduledJobDetail(scheduledJobName);
223
224 boolean jobIsIncomplete = isIncomplete(scheduledJobDetail);
225 if (jobIsIncomplete) {
226 log.append("\n\t").append(scheduledJobDetail.getFullName());
227 hasIncompleteJob = true;
228 }
229 }
230 if (hasIncompleteJob) {
231 LOG.info(log);
232 }
233 return hasIncompleteJob;
234 }
235
236 protected boolean isIncomplete(JobDetail scheduledJobDetail) {
237 if ( scheduledJobDetail == null ) {
238 return false;
239 }
240
241 return !SCHEDULE_JOB_NAME.equals(scheduledJobDetail.getName()) && (isPending(scheduledJobDetail) || isScheduled(scheduledJobDetail));
242 }
243
244
245
246
247 @Override
248 public boolean isPastScheduleCutoffTime() {
249 return isPastScheduleCutoffTime(dateTimeService.getCurrentCalendar(), true);
250 }
251
252 protected boolean isPastScheduleCutoffTime(Calendar dateTime, boolean log) {
253 try {
254 Date scheduleCutoffTimeTemp = scheduler.getTriggersOfJob(SCHEDULE_JOB_NAME, SCHEDULED_GROUP)[0].getPreviousFireTime();
255 Calendar scheduleCutoffTime;
256 if (scheduleCutoffTimeTemp == null) {
257 scheduleCutoffTime = dateTimeService.getCurrentCalendar();
258 }
259 else {
260 scheduleCutoffTime = dateTimeService.getCalendar(scheduleCutoffTimeTemp);
261 }
262 String cutoffParameter = parameterService.getParameterValueAsString(ScheduleStep.class, OLEConstants.SystemGroupParameterNames.BATCH_SCHEDULE_CUTOFF_TIME);
263 String[] scheduleStepCutoffTime = StringUtils.split(cutoffParameter, ":");
264 if ( scheduleStepCutoffTime.length != 3 && scheduleStepCutoffTime.length != 4 ) {
265 throw new IllegalArgumentException( "Error! The " + OLEConstants.SystemGroupParameterNames.BATCH_SCHEDULE_CUTOFF_TIME + " parameter had an invalid value: " + cutoffParameter );
266 }
267
268
269 if ( scheduleStepCutoffTime.length == 4 ) {
270 int hour = Integer.parseInt(scheduleStepCutoffTime[0]);
271
272 if ( hour == 12 ) {
273 hour = 0;
274 } else {
275 hour--;
276 }
277 scheduleCutoffTime.set(Calendar.HOUR, hour );
278 if ( StringUtils.containsIgnoreCase(scheduleStepCutoffTime[3], "AM") ) {
279 scheduleCutoffTime.set(Calendar.AM_PM, Calendar.AM);
280 } else {
281 scheduleCutoffTime.set(Calendar.AM_PM, Calendar.PM);
282 }
283 } else {
284 scheduleCutoffTime.set(Calendar.HOUR_OF_DAY, Integer.parseInt(scheduleStepCutoffTime[0]));
285 }
286 scheduleCutoffTime.set(Calendar.MINUTE, Integer.parseInt(scheduleStepCutoffTime[1]));
287 scheduleCutoffTime.set(Calendar.SECOND, Integer.parseInt(scheduleStepCutoffTime[2]));
288 if (parameterService.getParameterValueAsBoolean(ScheduleStep.class, OLEConstants.SystemGroupParameterNames.BATCH_SCHEDULE_CUTOFF_TIME_IS_NEXT_DAY)) {
289 scheduleCutoffTime.add(Calendar.DAY_OF_YEAR, 1);
290 }
291 boolean isPastScheduleCutoffTime = dateTime.after(scheduleCutoffTime);
292 if (log) {
293 LOG.info(new StringBuilder("isPastScheduleCutoffTime=").append(isPastScheduleCutoffTime).append(" : ").append(dateTimeService.toDateTimeString(dateTime.getTime())).append(" / ").append(dateTimeService.toDateTimeString(scheduleCutoffTime.getTime())));
294 }
295 return isPastScheduleCutoffTime;
296 }
297 catch (NumberFormatException e) {
298 throw new RuntimeException("Caught exception while checking whether we've exceeded the schedule cutoff time", e);
299 }
300 catch (SchedulerException e) {
301 throw new RuntimeException("Caught exception while checking whether we've exceeded the schedule cutoff time", e);
302 }
303 }
304
305
306
307
308 @Override
309 public void processWaitingJobs() {
310 for ( String scheduledJobName : getJobNamesForScheduleJob() ) {
311 JobDetail jobDetail = getScheduledJobDetail(scheduledJobName);
312 if (isPending(jobDetail)) {
313 if (shouldScheduleJob(jobDetail)) {
314 scheduleJob(SCHEDULED_GROUP, scheduledJobName, 0, 0, new Date(), null, Collections.singletonMap(Job.MASTER_JOB_NAME, SCHEDULE_JOB_NAME) );
315 }
316 if (shouldCancelJob(jobDetail)) {
317 updateStatus(SCHEDULED_GROUP, scheduledJobName, CANCELLED_JOB_STATUS_CODE);
318 }
319 }
320 }
321 }
322
323
324
325
326 @Override
327 public void logScheduleResults() {
328 StringBuilder scheduleResults = new StringBuilder("The schedule completed.");
329 for ( String scheduledJobName : getJobNamesForScheduleJob() ) {
330 JobDetail jobDetail = getScheduledJobDetail(scheduledJobName);
331 if ( jobDetail != null && !SCHEDULE_JOB_NAME.equals(jobDetail.getName())) {
332 scheduleResults.append("\n\t").append(jobDetail.getName()).append("=").append(getStatus(jobDetail));
333 }
334 }
335 LOG.info(scheduleResults);
336 }
337
338
339
340
341 @Override
342 public boolean shouldNotRun(JobDetail jobDetail) {
343 if (SCHEDULED_GROUP.equals(jobDetail.getGroup())) {
344 if (isCancelled(jobDetail)) {
345 if ( LOG.isInfoEnabled() ) {
346 LOG.info("Telling listener not to run job, because it has been cancelled: " + jobDetail.getName());
347 }
348 return true;
349 }
350 else {
351 for (String dependencyJobName : getJobDependencies(jobDetail.getName()).keySet()) {
352 if (!isDependencySatisfiedPositively(jobDetail, getScheduledJobDetail(dependencyJobName))) {
353 if ( LOG.isInfoEnabled() ) {
354 LOG.info("Telling listener not to run job, because a dependency has not been satisfied positively: "+jobDetail.getName()+" (dependency job = "+dependencyJobName+")");
355 }
356 return true;
357 }
358 }
359 }
360 }
361 return false;
362 }
363
364
365
366
367 @Override
368 public void updateStatus(JobDetail jobDetail, String jobStatus) {
369 if ( LOG.isInfoEnabled() ) {
370 LOG.info("Updating status of job: "+jobDetail.getName()+"="+jobStatus);
371 }
372 jobDetail.getJobDataMap().put(JOB_STATUS_PARAMETER, jobStatus);
373 }
374
375 @Override
376 public void runJob(String jobName, String requestorEmailAddress) {
377 runJob(jobName, 0, 0, new Date(), requestorEmailAddress);
378 }
379
380 @Override
381 public void runJob(String jobName, int startStep, int stopStep, Date startTime, String requestorEmailAddress) {
382 runJob(UNSCHEDULED_GROUP, jobName, startStep, stopStep, startTime, requestorEmailAddress);
383 }
384
385 @Override
386 public void runJob(String groupName, String jobName, int startStep, int stopStep, Date jobStartTime, String requestorEmailAddress) {
387 if ( LOG.isInfoEnabled() ) {
388 LOG.info("Executing user initiated job: " + groupName + "." + jobName + " (startStep=" + startStep + " / stopStep=" + stopStep + " / startTime=" + jobStartTime + " / requestorEmailAddress=" + requestorEmailAddress + ")");
389 }
390
391 try {
392 JobDetail jobDetail = scheduler.getJobDetail(jobName, groupName);
393 scheduleJob(groupName, jobName, startStep, stopStep, jobStartTime, requestorEmailAddress, null);
394 }
395 catch (SchedulerException ex) {
396 throw new RuntimeException("Unable to run a job directly", ex);
397 }
398 }
399
400 public void runStep(String groupName, String jobName, String stepName, Date startTime, String requestorEmailAddress) {
401 if ( LOG.isInfoEnabled() ) {
402 LOG.info("Executing user initiated step: " + stepName + " / requestorEmailAddress=" + requestorEmailAddress);
403 }
404
405
406 if (isJobRunning(jobName)) {
407 LOG.warn("Attempt to run job already executing, aborting");
408 return;
409 }
410 int stepNum = 1;
411 boolean stepFound = false;
412 BatchJobStatus job = getJob(groupName, jobName);
413 for (Step step : job.getSteps()) {
414 if (step.getName().equals(stepName)) {
415 stepFound = true;
416 break;
417 }
418 stepNum++;
419 }
420 if (stepFound) {
421 runJob(groupName, jobName, stepNum, stepNum, startTime, requestorEmailAddress);
422 }
423 else {
424 LOG.warn("Unable to find step " + stepName + " in job " + groupName + "." + jobName);
425 }
426 }
427
428 @Override
429 public boolean isJobRunning(String jobName) {
430 List<JobExecutionContext> runningJobs = getRunningJobs();
431 for (JobExecutionContext jobCtx : runningJobs) {
432 if (jobCtx.getJobDetail().getName().equals(jobName)) {
433 return true;
434 }
435 }
436 return false;
437 }
438
439 protected void addJob(JobDetail jobDetail) {
440 try {
441 if ( LOG.isInfoEnabled() ) {
442 LOG.info("Adding job: " + jobDetail.getFullName());
443 }
444 scheduler.addJob(jobDetail, true);
445 }
446 catch (SchedulerException e) {
447 throw new RuntimeException("Caught exception while adding job: " + jobDetail.getFullName(), e);
448 }
449 }
450
451 protected void addTrigger(Trigger trigger) {
452 try {
453 if (UNSCHEDULED_GROUP.equals(trigger.getGroup())) {
454 LOG.error("Triggers should not be specified for jobs in the unscheduled group - not adding trigger: " + trigger.getName());
455 }
456 else {
457 LOG.info("Adding trigger: " + trigger.getName());
458 try {
459 scheduler.scheduleJob(trigger);
460 }
461 catch (ObjectAlreadyExistsException ex) {
462 }
463 }
464 }
465 catch (SchedulerException e) {
466 throw new RuntimeException("Caught exception while adding trigger: " + trigger.getFullName(), e);
467 }
468 }
469
470 protected void scheduleJob(String groupName, String jobName, int startStep, int endStep, Date startTime, String requestorEmailAddress, Map<String,String> additionalJobData ) {
471 try {
472 updateStatus(groupName, jobName, SchedulerService.SCHEDULED_JOB_STATUS_CODE);
473 SimpleTriggerDescriptor trigger = new SimpleTriggerDescriptor(jobName, groupName, jobName, dateTimeService);
474 trigger.setStartTime(startTime);
475 Trigger qTrigger = trigger.getTrigger();
476 qTrigger.getJobDataMap().put(JobListener.REQUESTOR_EMAIL_ADDRESS_KEY, requestorEmailAddress);
477 qTrigger.getJobDataMap().put(Job.JOB_RUN_START_STEP, String.valueOf(startStep));
478 qTrigger.getJobDataMap().put(Job.JOB_RUN_END_STEP, String.valueOf(endStep));
479 if ( additionalJobData != null ) {
480 qTrigger.getJobDataMap().putAll(additionalJobData);
481 }
482 for (Trigger oldTrigger : scheduler.getTriggersOfJob(jobName, groupName)) {
483 scheduler.unscheduleJob(oldTrigger.getName(), groupName);
484 }
485 scheduler.scheduleJob(qTrigger);
486 }
487 catch (SchedulerException e) {
488 throw new RuntimeException("Caught exception while scheduling job: " + jobName, e);
489 }
490 }
491
492 protected boolean shouldScheduleJob(JobDetail jobDetail) {
493 try {
494 if (scheduler.getTriggersOfJob(jobDetail.getName(), SCHEDULED_GROUP).length > 0) {
495 return false;
496 }
497 for (String dependencyJobName : getJobDependencies(jobDetail.getName()).keySet()) {
498 JobDetail dependencyJobDetail = getScheduledJobDetail(dependencyJobName);
499 if ( dependencyJobDetail == null ) {
500 LOG.error( "Unable to get JobDetail for dependency of " + jobDetail.getName() + " : " + dependencyJobName );
501 return false;
502 }
503 if (!isDependencySatisfiedPositively(jobDetail, dependencyJobDetail)) {
504 return false;
505 }
506 }
507 }
508 catch (SchedulerException se) {
509 throw new RuntimeException("Caught scheduler exception while determining whether to schedule job: " + jobDetail.getName(), se);
510 }
511 return true;
512 }
513
514 protected boolean shouldCancelJob(JobDetail jobDetail) {
515 LOG.info("shouldCancelJob:::::: " + jobDetail.getFullName());
516 if ( jobDetail == null ) {
517 return true;
518 }
519 for (String dependencyJobName : getJobDependencies(jobDetail.getName()).keySet()) {
520 LOG.info("dependencyJobName:::::" + dependencyJobName);
521 JobDetail dependencyJobDetail = getScheduledJobDetail(dependencyJobName);
522 if (isDependencySatisfiedNegatively(jobDetail, dependencyJobDetail)) {
523 return true;
524 }
525 }
526 return false;
527 }
528
529 protected boolean isDependencySatisfiedPositively(JobDetail dependentJobDetail, JobDetail dependencyJobDetail) {
530 if ( dependentJobDetail == null || dependencyJobDetail == null ) {
531 return false;
532 }
533 return isSucceeded(dependencyJobDetail) || ((isFailed(dependencyJobDetail) || isCancelled(dependencyJobDetail)) && isSoftDependency(dependentJobDetail.getName(), dependencyJobDetail.getName()));
534 }
535
536 protected boolean isDependencySatisfiedNegatively(JobDetail dependentJobDetail, JobDetail dependencyJobDetail) {
537 LOG.info("isDependencySatisfiedNegatively:::: dependentJobDetail::: " + dependencyJobDetail.getFullName() + " dependencyJobDetail " + dependencyJobDetail.getFullName() );
538 if ( dependentJobDetail == null || dependencyJobDetail == null ) {
539 return true;
540 }
541 return (isFailed(dependencyJobDetail) || isCancelled(dependencyJobDetail)) && !isSoftDependency(dependentJobDetail.getName(), dependencyJobDetail.getName());
542 }
543
544 protected boolean isSoftDependency(String dependentJobName, String dependencyJobName) {
545 return SOFT_DEPENDENCY_CODE.equals(getJobDependencies(dependentJobName).get(dependencyJobName));
546 }
547
548 protected Map<String, String> getJobDependencies(String jobName) {
549 LOG.info("getJobDependencies:::: for job " + jobName);
550 return BatchSpringContext.getJobDescriptor(jobName).getDependencies();
551 }
552
553 protected boolean isPending(JobDetail jobDetail) {
554 return getStatus(jobDetail) == null;
555 }
556
557 protected boolean isScheduled(JobDetail jobDetail) {
558 return SCHEDULED_JOB_STATUS_CODE.equals(getStatus(jobDetail));
559 }
560
561 protected boolean isSucceeded(JobDetail jobDetail) {
562 return SUCCEEDED_JOB_STATUS_CODE.equals(getStatus(jobDetail));
563 }
564
565 protected boolean isFailed(JobDetail jobDetail) {
566 return FAILED_JOB_STATUS_CODE.equals(getStatus(jobDetail));
567 }
568
569 protected boolean isCancelled(JobDetail jobDetail) {
570 return CANCELLED_JOB_STATUS_CODE.equals(getStatus(jobDetail));
571 }
572
573 @Override
574 public String getStatus(JobDetail jobDetail) {
575 if ( jobDetail == null ) {
576 return FAILED_JOB_STATUS_CODE;
577 }
578 OleModuleServiceImpl moduleService = (OleModuleServiceImpl)
579 SpringContext.getBean(KualiModuleService.class).getResponsibleModuleServiceForJob(jobDetail.getName());
580
581
582 return (moduleService!=null && moduleService.isExternalJob(jobDetail.getName()))
583 ? moduleService.getExternalJobStatus(jobDetail.getName())
584 : jobDetail.getJobDataMap().getString(SchedulerServiceImpl.JOB_STATUS_PARAMETER);
585 }
586
587 protected JobDetail getScheduledJobDetail(String jobName) {
588 LOG.info("getScheduledJobDetail ::::::: " + jobName);
589 try {
590 JobDetail jobDetail = scheduler.getJobDetail(jobName, SCHEDULED_GROUP);
591 if ( jobDetail == null ) {
592 LOG.error( "Unable to obtain the job details for the scheduled version of: " + jobName );
593 }
594 return jobDetail;
595 }
596 catch (SchedulerException e) {
597 throw new RuntimeException("Caught scheduler exception while getting job detail: " + jobName, e);
598 }
599 }
600
601
602
603
604
605
606 @Override
607 public void setScheduler(Scheduler scheduler) {
608 this.scheduler = scheduler;
609 }
610
611 public void setParameterService(ParameterService parameterService) {
612 this.parameterService = parameterService;
613 }
614
615
616
617
618
619
620 public void setDateTimeService(DateTimeService dateTimeService) {
621 this.dateTimeService = dateTimeService;
622 }
623
624
625
626
627
628
629 public void setKualiModuleService(KualiModuleService moduleService) {
630 this.kualiModuleService = moduleService;
631 }
632
633
634
635
636
637
638 public void setJobListener(JobListener jobListener) {
639 this.jobListener = jobListener;
640 }
641
642 @Override
643 public List<BatchJobStatus> getJobs() {
644 ArrayList<BatchJobStatus> jobs = new ArrayList<BatchJobStatus>();
645 try {
646 for (String jobGroup : scheduler.getJobGroupNames()) {
647 for (String jobName : scheduler.getJobNames(jobGroup)) {
648 try {
649 JobDescriptor jobDescriptor = retrieveJobDescriptor(jobName);
650 JobDetail jobDetail = scheduler.getJobDetail(jobName, jobGroup);
651 jobs.add(new BatchJobStatus(jobDescriptor, jobDetail));
652 }
653 catch (NoSuchBeanDefinitionException ex) {
654
655 LOG.warn("Attempt to find bean " + jobGroup + "." + jobName + " failed - not in Spring context");
656 }
657 }
658 }
659 }
660 catch (SchedulerException ex) {
661 throw new RuntimeException("Exception while obtaining job list", ex);
662 }
663 return jobs;
664 }
665
666 @Override
667 public BatchJobStatus getJob(String groupName, String jobName) {
668 for (BatchJobStatus job : getJobs()) {
669 if (job.getName().equals(jobName) && job.getGroup().equals(groupName)) {
670 return job;
671 }
672 }
673 return null;
674 }
675
676 @Override
677 public List<BatchJobStatus> getJobs(String groupName) {
678 ArrayList<BatchJobStatus> jobs = new ArrayList<BatchJobStatus>();
679 try {
680 for (String jobName : scheduler.getJobNames(groupName)) {
681 try {
682 JobDescriptor jobDescriptor = retrieveJobDescriptor(jobName);
683 JobDetail jobDetail = scheduler.getJobDetail(jobName, groupName);
684 jobs.add(new BatchJobStatus(jobDescriptor, jobDetail));
685 }
686 catch (NoSuchBeanDefinitionException ex) {
687
688 LOG.warn("Attempt to find bean " + groupName + "." + jobName + " failed - not in Spring context");
689 }
690 }
691 }
692 catch (SchedulerException ex) {
693 throw new RuntimeException("Exception while obtaining job list", ex);
694 }
695 return jobs;
696 }
697
698 @Override
699 public List<JobExecutionContext> getRunningJobs() {
700 try {
701 List<JobExecutionContext> jobContexts = scheduler.getCurrentlyExecutingJobs();
702 return jobContexts;
703 }
704 catch (SchedulerException ex) {
705 throw new RuntimeException("Unable to get list of running jobs.", ex);
706 }
707 }
708
709 protected void updateStatus(String groupName, String jobName, String jobStatus) {
710 try {
711 JobDetail jobDetail = scheduler.getJobDetail(jobName, groupName);
712 updateStatus(jobDetail, jobStatus);
713 scheduler.addJob(jobDetail, true);
714 }
715 catch (SchedulerException e) {
716 throw new RuntimeException(new StringBuilder("Caught scheduler exception while updating job status: ").append(jobName).append(", ").append(jobStatus).toString(), e);
717 }
718 }
719
720 @Override
721 public void removeScheduled(String jobName) {
722 try {
723 scheduler.deleteJob(jobName, SCHEDULED_GROUP);
724 }
725 catch (SchedulerException ex) {
726 throw new RuntimeException("Unable to remove scheduled job: " + jobName, ex);
727 }
728 }
729
730 @Override
731 public void addScheduled(JobDetail job) {
732 try {
733 job.setGroup(SCHEDULED_GROUP);
734 scheduler.addJob(job, true);
735 }
736 catch (SchedulerException ex) {
737 throw new RuntimeException("Unable to add job to scheduled group: " + job.getName(), ex);
738 }
739 }
740
741 @Override
742 public void addUnscheduled(JobDetail job) {
743 try {
744 job.setGroup(UNSCHEDULED_GROUP);
745 scheduler.addJob(job, true);
746 }
747 catch (SchedulerException ex) {
748 throw new RuntimeException("Unable to add job to unscheduled group: " + job.getName(), ex);
749 }
750 }
751
752 @Override
753 public List<String> getSchedulerGroups() {
754 try {
755 return Arrays.asList(scheduler.getJobGroupNames());
756 }
757 catch (SchedulerException ex) {
758 throw new RuntimeException("Exception while obtaining job list", ex);
759 }
760 }
761
762 @Override
763 public List<String> getJobStatuses() {
764 return jobStatuses;
765 }
766
767 @Override
768 public void interruptJob(String jobName) {
769 List<JobExecutionContext> runningJobs = getRunningJobs();
770 for (JobExecutionContext jobCtx : runningJobs) {
771 if (jobName.equals(jobCtx.getJobDetail().getName())) {
772
773 try {
774 ((Job) jobCtx.getJobInstance()).interrupt();
775 }
776 catch (UnableToInterruptJobException ex) {
777 LOG.warn("Unable to perform job interrupt", ex);
778 }
779 break;
780 }
781 }
782
783 }
784
785 @Override
786 public Date getNextStartTime(BatchJobStatus job) {
787 try {
788 Trigger[] triggers = scheduler.getTriggersOfJob(job.getName(), job.getGroup());
789 Date nextDate = new Date(Long.MAX_VALUE);
790 for (Trigger trigger : triggers) {
791 if (trigger.getNextFireTime() != null){
792 if (trigger.getNextFireTime().getTime() < nextDate.getTime()) {
793 nextDate = trigger.getNextFireTime();
794 }
795 }
796 }
797 if (nextDate.getTime() == Long.MAX_VALUE) {
798 nextDate = null;
799 }
800 return nextDate;
801 }
802 catch (SchedulerException ex) {
803
804 }
805 return null;
806 }
807
808 @Override
809 public Date getNextStartTime(String groupName, String jobName) {
810 BatchJobStatus job = getJob(groupName, jobName);
811
812 return getNextStartTime(job);
813 }
814
815 public void setMailService(MailService mailService) {
816 this.mailService = mailService;
817 }
818
819 protected JobDescriptor retrieveJobDescriptor(String jobName) {
820 if (externalizedJobDescriptors.containsKey(jobName)) {
821 return externalizedJobDescriptors.get(jobName);
822 }
823 return BatchSpringContext.getJobDescriptor(jobName);
824 }
825
826 ThreadLocal<List<String>> jobNamesForScheduleJob = new ThreadLocal<List<String>>();
827
828 protected List<String> getJobNamesForScheduleJob() {
829 List<String> jobNames = new ArrayList<String>();
830 try {
831 for (String scheduledJobName : scheduler.getJobNames(SCHEDULED_GROUP)) {
832 if (scheduler.getTriggersOfJob(scheduledJobName, SCHEDULED_GROUP).length == 0) {
833
834 jobNames.add( scheduledJobName );
835 }
836 }
837 } catch (Exception ex) {
838 LOG.error("Error occurred while initializing job name list", ex);
839 }
840 return jobNames;
841 }
842
843 @Override
844 public void reinitializeScheduledJobs() {
845 try {
846 for (String scheduledJobName : getJobNamesForScheduleJob() ) {
847 updateStatus(SCHEDULED_GROUP, scheduledJobName, null);
848 }
849 } catch (Exception e) {
850 LOG.error("Error occurred while trying to reinitialize jobs", e);
851 }
852 }
853 }