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 Map<String, String> defaultMap = new HashMap();
551 if (BatchSpringContext.getJobDescriptor(jobName) != null && BatchSpringContext.getJobDescriptor(jobName).getDependencies() != null) {
552 return BatchSpringContext.getJobDescriptor(jobName).getDependencies();
553 } else {
554 return defaultMap;
555 }
556 }
557
558 protected boolean isPending(JobDetail jobDetail) {
559 return getStatus(jobDetail) == null;
560 }
561
562 protected boolean isScheduled(JobDetail jobDetail) {
563 return SCHEDULED_JOB_STATUS_CODE.equals(getStatus(jobDetail));
564 }
565
566 protected boolean isSucceeded(JobDetail jobDetail) {
567 return SUCCEEDED_JOB_STATUS_CODE.equals(getStatus(jobDetail));
568 }
569
570 protected boolean isFailed(JobDetail jobDetail) {
571 return FAILED_JOB_STATUS_CODE.equals(getStatus(jobDetail));
572 }
573
574 protected boolean isCancelled(JobDetail jobDetail) {
575 return CANCELLED_JOB_STATUS_CODE.equals(getStatus(jobDetail));
576 }
577
578 @Override
579 public String getStatus(JobDetail jobDetail) {
580 if ( jobDetail == null ) {
581 return FAILED_JOB_STATUS_CODE;
582 }
583 OleModuleServiceImpl moduleService = (OleModuleServiceImpl)
584 SpringContext.getBean(KualiModuleService.class).getResponsibleModuleServiceForJob(jobDetail.getName());
585
586
587 return (moduleService!=null && moduleService.isExternalJob(jobDetail.getName()))
588 ? moduleService.getExternalJobStatus(jobDetail.getName())
589 : jobDetail.getJobDataMap().getString(SchedulerServiceImpl.JOB_STATUS_PARAMETER);
590 }
591
592 protected JobDetail getScheduledJobDetail(String jobName) {
593 LOG.info("getScheduledJobDetail ::::::: " + jobName);
594 try {
595 JobDetail jobDetail = scheduler.getJobDetail(jobName, SCHEDULED_GROUP);
596 if ( jobDetail == null ) {
597 LOG.error( "Unable to obtain the job details for the scheduled version of: " + jobName );
598 }
599 return jobDetail;
600 }
601 catch (SchedulerException e) {
602 throw new RuntimeException("Caught scheduler exception while getting job detail: " + jobName, e);
603 }
604 }
605
606
607
608
609
610
611 @Override
612 public void setScheduler(Scheduler scheduler) {
613 this.scheduler = scheduler;
614 }
615
616 public void setParameterService(ParameterService parameterService) {
617 this.parameterService = parameterService;
618 }
619
620
621
622
623
624
625 public void setDateTimeService(DateTimeService dateTimeService) {
626 this.dateTimeService = dateTimeService;
627 }
628
629
630
631
632
633
634 public void setKualiModuleService(KualiModuleService moduleService) {
635 this.kualiModuleService = moduleService;
636 }
637
638
639
640
641
642
643 public void setJobListener(JobListener jobListener) {
644 this.jobListener = jobListener;
645 }
646
647 @Override
648 public List<BatchJobStatus> getJobs() {
649 ArrayList<BatchJobStatus> jobs = new ArrayList<BatchJobStatus>();
650 try {
651 for (String jobGroup : scheduler.getJobGroupNames()) {
652 for (String jobName : scheduler.getJobNames(jobGroup)) {
653 try {
654 JobDescriptor jobDescriptor = retrieveJobDescriptor(jobName);
655 JobDetail jobDetail = scheduler.getJobDetail(jobName, jobGroup);
656 jobs.add(new BatchJobStatus(jobDescriptor, jobDetail));
657 }
658 catch (NoSuchBeanDefinitionException ex) {
659
660 LOG.warn("Attempt to find bean " + jobGroup + "." + jobName + " failed - not in Spring context");
661 }
662 }
663 }
664 }
665 catch (SchedulerException ex) {
666 throw new RuntimeException("Exception while obtaining job list", ex);
667 }
668 return jobs;
669 }
670
671 @Override
672 public BatchJobStatus getJob(String groupName, String jobName) {
673 for (BatchJobStatus job : getJobs()) {
674 if (job.getName().equals(jobName) && job.getGroup().equals(groupName)) {
675 return job;
676 }
677 }
678 return null;
679 }
680
681 @Override
682 public List<BatchJobStatus> getJobs(String groupName) {
683 ArrayList<BatchJobStatus> jobs = new ArrayList<BatchJobStatus>();
684 try {
685 for (String jobName : scheduler.getJobNames(groupName)) {
686 try {
687 JobDescriptor jobDescriptor = retrieveJobDescriptor(jobName);
688 JobDetail jobDetail = scheduler.getJobDetail(jobName, groupName);
689 jobs.add(new BatchJobStatus(jobDescriptor, jobDetail));
690 }
691 catch (NoSuchBeanDefinitionException ex) {
692
693 LOG.warn("Attempt to find bean " + groupName + "." + jobName + " failed - not in Spring context");
694 }
695 }
696 }
697 catch (SchedulerException ex) {
698 throw new RuntimeException("Exception while obtaining job list", ex);
699 }
700 return jobs;
701 }
702
703 @Override
704 public List<JobExecutionContext> getRunningJobs() {
705 try {
706 List<JobExecutionContext> jobContexts = scheduler.getCurrentlyExecutingJobs();
707 return jobContexts;
708 }
709 catch (SchedulerException ex) {
710 throw new RuntimeException("Unable to get list of running jobs.", ex);
711 }
712 }
713
714 protected void updateStatus(String groupName, String jobName, String jobStatus) {
715 try {
716 JobDetail jobDetail = scheduler.getJobDetail(jobName, groupName);
717 updateStatus(jobDetail, jobStatus);
718 scheduler.addJob(jobDetail, true);
719 }
720 catch (SchedulerException e) {
721 throw new RuntimeException(new StringBuilder("Caught scheduler exception while updating job status: ").append(jobName).append(", ").append(jobStatus).toString(), e);
722 }
723 }
724
725 @Override
726 public void removeScheduled(String jobName) {
727 try {
728 scheduler.deleteJob(jobName, SCHEDULED_GROUP);
729 }
730 catch (SchedulerException ex) {
731 throw new RuntimeException("Unable to remove scheduled job: " + jobName, ex);
732 }
733 }
734
735 @Override
736 public void addScheduled(JobDetail job) {
737 try {
738 job.setGroup(SCHEDULED_GROUP);
739 scheduler.addJob(job, true);
740 }
741 catch (SchedulerException ex) {
742 throw new RuntimeException("Unable to add job to scheduled group: " + job.getName(), ex);
743 }
744 }
745
746 @Override
747 public void addUnscheduled(JobDetail job) {
748 try {
749 job.setGroup(UNSCHEDULED_GROUP);
750 scheduler.addJob(job, true);
751 }
752 catch (SchedulerException ex) {
753 throw new RuntimeException("Unable to add job to unscheduled group: " + job.getName(), ex);
754 }
755 }
756
757 @Override
758 public List<String> getSchedulerGroups() {
759 try {
760 return Arrays.asList(scheduler.getJobGroupNames());
761 }
762 catch (SchedulerException ex) {
763 throw new RuntimeException("Exception while obtaining job list", ex);
764 }
765 }
766
767 @Override
768 public List<String> getJobStatuses() {
769 return jobStatuses;
770 }
771
772 @Override
773 public void interruptJob(String jobName) {
774 List<JobExecutionContext> runningJobs = getRunningJobs();
775 for (JobExecutionContext jobCtx : runningJobs) {
776 if (jobName.equals(jobCtx.getJobDetail().getName())) {
777
778 try {
779 ((Job) jobCtx.getJobInstance()).interrupt();
780 }
781 catch (UnableToInterruptJobException ex) {
782 LOG.warn("Unable to perform job interrupt", ex);
783 }
784 break;
785 }
786 }
787
788 }
789
790 @Override
791 public Date getNextStartTime(BatchJobStatus job) {
792 try {
793 Trigger[] triggers = scheduler.getTriggersOfJob(job.getName(), job.getGroup());
794 Date nextDate = new Date(Long.MAX_VALUE);
795 for (Trigger trigger : triggers) {
796 if (trigger.getNextFireTime() != null){
797 if (trigger.getNextFireTime().getTime() < nextDate.getTime()) {
798 nextDate = trigger.getNextFireTime();
799 }
800 }
801 }
802 if (nextDate.getTime() == Long.MAX_VALUE) {
803 nextDate = null;
804 }
805 return nextDate;
806 }
807 catch (SchedulerException ex) {
808
809 }
810 return null;
811 }
812
813 @Override
814 public Date getNextStartTime(String groupName, String jobName) {
815 BatchJobStatus job = getJob(groupName, jobName);
816
817 return getNextStartTime(job);
818 }
819
820 public void setMailService(MailService mailService) {
821 this.mailService = mailService;
822 }
823
824 protected JobDescriptor retrieveJobDescriptor(String jobName) {
825 if (externalizedJobDescriptors.containsKey(jobName)) {
826 return externalizedJobDescriptors.get(jobName);
827 }
828 return BatchSpringContext.getJobDescriptor(jobName);
829 }
830
831 ThreadLocal<List<String>> jobNamesForScheduleJob = new ThreadLocal<List<String>>();
832
833 protected List<String> getJobNamesForScheduleJob() {
834 List<String> jobNames = new ArrayList<String>();
835 try {
836 for (String scheduledJobName : scheduler.getJobNames(SCHEDULED_GROUP)) {
837 if (scheduler.getTriggersOfJob(scheduledJobName, SCHEDULED_GROUP).length == 0) {
838
839 jobNames.add( scheduledJobName );
840 }
841 }
842 } catch (Exception ex) {
843 LOG.error("Error occurred while initializing job name list", ex);
844 }
845 return jobNames;
846 }
847
848 @Override
849 public void reinitializeScheduledJobs() {
850 try {
851 for (String scheduledJobName : getJobNamesForScheduleJob() ) {
852 updateStatus(SCHEDULED_GROUP, scheduledJobName, null);
853 }
854 } catch (Exception e) {
855 LOG.error("Error occurred while trying to reinitialize jobs", e);
856 }
857 }
858 }