001
014
015 package com.liferay.portal.scheduler.quartz;
016
017 import com.liferay.portal.kernel.bean.BeanReference;
018 import com.liferay.portal.kernel.dao.db.DB;
019 import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
020 import com.liferay.portal.kernel.json.JSONFactoryUtil;
021 import com.liferay.portal.kernel.log.Log;
022 import com.liferay.portal.kernel.log.LogFactoryUtil;
023 import com.liferay.portal.kernel.messaging.Message;
024 import com.liferay.portal.kernel.scheduler.IntervalTrigger;
025 import com.liferay.portal.kernel.scheduler.JobState;
026 import com.liferay.portal.kernel.scheduler.JobStateSerializeUtil;
027 import com.liferay.portal.kernel.scheduler.SchedulerEngine;
028 import com.liferay.portal.kernel.scheduler.SchedulerException;
029 import com.liferay.portal.kernel.scheduler.StorageType;
030 import com.liferay.portal.kernel.scheduler.TriggerFactoryUtil;
031 import com.liferay.portal.kernel.scheduler.TriggerState;
032 import com.liferay.portal.kernel.scheduler.TriggerType;
033 import com.liferay.portal.kernel.scheduler.messaging.SchedulerResponse;
034 import com.liferay.portal.kernel.util.CharPool;
035 import com.liferay.portal.kernel.util.ServerDetector;
036 import com.liferay.portal.kernel.util.StringPool;
037 import com.liferay.portal.kernel.util.Time;
038 import com.liferay.portal.scheduler.job.MessageSenderJob;
039 import com.liferay.portal.service.QuartzLocalService;
040 import com.liferay.portal.util.PropsUtil;
041 import com.liferay.portal.util.PropsValues;
042
043 import java.text.ParseException;
044
045 import java.util.ArrayList;
046 import java.util.Collections;
047 import java.util.Date;
048 import java.util.List;
049 import java.util.Map;
050 import java.util.Properties;
051 import java.util.Set;
052
053 import org.quartz.CronScheduleBuilder;
054 import org.quartz.CronTrigger;
055 import org.quartz.JobBuilder;
056 import org.quartz.JobDataMap;
057 import org.quartz.JobDetail;
058 import org.quartz.JobKey;
059 import org.quartz.ObjectAlreadyExistsException;
060 import org.quartz.Scheduler;
061 import org.quartz.SimpleScheduleBuilder;
062 import org.quartz.SimpleTrigger;
063 import org.quartz.Trigger;
064 import org.quartz.TriggerBuilder;
065 import org.quartz.TriggerKey;
066 import org.quartz.impl.StdSchedulerFactory;
067 import org.quartz.impl.jdbcjobstore.UpdateLockRowSemaphore;
068 import org.quartz.impl.matchers.GroupMatcher;
069
070
077 public class QuartzSchedulerEngine implements SchedulerEngine {
078
079 public void afterPropertiesSet() {
080 if (!PropsValues.SCHEDULER_ENABLED) {
081 return;
082 }
083
084 try {
085 quartzLocalService.checkQuartzTables();
086
087 _persistedScheduler = initializeScheduler(
088 "persisted.scheduler.", true);
089
090 _memoryScheduler = initializeScheduler("memory.scheduler.", false);
091 }
092 catch (Exception e) {
093 _log.error("Unable to initialize engine", e);
094 }
095 }
096
097 public void delete(String groupName) throws SchedulerException {
098 if (!PropsValues.SCHEDULER_ENABLED) {
099 return;
100 }
101
102 try {
103 Scheduler scheduler = getScheduler(groupName);
104
105 groupName = fixMaxLength(
106 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
107
108 Set<JobKey> jobKeys = scheduler.getJobKeys(
109 GroupMatcher.jobGroupEquals(groupName));
110
111 for (JobKey jobKey : jobKeys) {
112 scheduler.deleteJob(jobKey);
113 }
114 }
115 catch (Exception e) {
116 throw new SchedulerException(
117 "Unable to delete jobs in group " + groupName, e);
118 }
119 }
120
121 public void delete(String jobName, String groupName)
122 throws SchedulerException {
123
124 if (!PropsValues.SCHEDULER_ENABLED) {
125 return;
126 }
127
128 try {
129 Scheduler scheduler = getScheduler(groupName);
130
131 jobName = fixMaxLength(jobName, JOB_NAME_MAX_LENGTH);
132 groupName = fixMaxLength(
133 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
134
135 JobKey jobKey = new JobKey(jobName, groupName);
136
137 scheduler.deleteJob(jobKey);
138 }
139 catch (Exception e) {
140 throw new SchedulerException(
141 "Unable to delete job {jobName=" + jobName + ", groupName=" +
142 groupName + "}",
143 e);
144 }
145 }
146
147 public void destroy() {
148 try {
149 shutdown();
150 }
151 catch (SchedulerException se) {
152 if (_log.isWarnEnabled()) {
153 _log.warn("Unable to shutdown", se);
154 }
155 }
156 }
157
158 public SchedulerResponse getScheduledJob(String jobName, String groupName)
159 throws SchedulerException {
160
161 if (!PropsValues.SCHEDULER_ENABLED) {
162 return null;
163 }
164
165 try {
166 Scheduler scheduler = getScheduler(groupName);
167
168 jobName = fixMaxLength(jobName, JOB_NAME_MAX_LENGTH);
169 groupName = fixMaxLength(
170 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
171
172 JobKey jobKey = new JobKey(jobName, groupName);
173
174 return getScheduledJob(scheduler, jobKey);
175 }
176 catch (Exception e) {
177 throw new SchedulerException(
178 "Unable to get job {jobName=" + jobName + ", groupName=" +
179 groupName + "}",
180 e);
181 }
182 }
183
184 public List<SchedulerResponse> getScheduledJobs()
185 throws SchedulerException {
186
187 if (!PropsValues.SCHEDULER_ENABLED) {
188 return Collections.emptyList();
189 }
190
191 try {
192 List<String> groupNames = _persistedScheduler.getJobGroupNames();
193
194 List<SchedulerResponse> schedulerResponses =
195 new ArrayList<SchedulerResponse>();
196
197 for (String groupName : groupNames) {
198 schedulerResponses.addAll(
199 getScheduledJobs(_persistedScheduler, groupName));
200 }
201
202 groupNames = _memoryScheduler.getJobGroupNames();
203
204 for (String groupName : groupNames) {
205 schedulerResponses.addAll(
206 getScheduledJobs(_memoryScheduler, groupName));
207 }
208
209 return schedulerResponses;
210 }
211 catch (Exception e) {
212 throw new SchedulerException("Unable to get jobs", e);
213 }
214 }
215
216 public List<SchedulerResponse> getScheduledJobs(String groupName)
217 throws SchedulerException {
218
219 if (!PropsValues.SCHEDULER_ENABLED) {
220 return Collections.emptyList();
221 }
222
223 try {
224 Scheduler scheduler = getScheduler(groupName);
225
226 return getScheduledJobs(scheduler, groupName);
227 }
228 catch (Exception e) {
229 throw new SchedulerException(
230 "Unable to get jobs in group " + groupName, e);
231 }
232 }
233
234 public void pause(String groupName) throws SchedulerException {
235 if (!PropsValues.SCHEDULER_ENABLED) {
236 return;
237 }
238
239 try {
240 Scheduler scheduler = getScheduler(groupName);
241
242 groupName = fixMaxLength(
243 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
244
245 Set<JobKey> jobKeys = scheduler.getJobKeys(
246 GroupMatcher.jobGroupEquals(groupName));
247
248 for (JobKey jobKey : jobKeys) {
249 updateJobState(scheduler, jobKey, TriggerState.PAUSED, false);
250 }
251
252 scheduler.pauseJobs(GroupMatcher.jobGroupEquals(groupName));
253 }
254 catch (Exception e) {
255 throw new SchedulerException(
256 "Unable to pause jobs in group " + groupName, e);
257 }
258 }
259
260 public void pause(String jobName, String groupName)
261 throws SchedulerException {
262
263 if (!PropsValues.SCHEDULER_ENABLED) {
264 return;
265 }
266
267 try {
268 Scheduler scheduler = getScheduler(groupName);
269
270 jobName = fixMaxLength(jobName, JOB_NAME_MAX_LENGTH);
271 groupName = fixMaxLength(
272 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
273
274 JobKey jobKey = new JobKey(jobName, groupName);
275
276 updateJobState(scheduler, jobKey, TriggerState.PAUSED, false);
277
278 scheduler.pauseJob(jobKey);
279 }
280 catch (Exception e) {
281 throw new SchedulerException(
282 "Unable to pause job {jobName=" + jobName + ", groupName=" +
283 groupName + "}",
284 e);
285 }
286 }
287
288 public void resume(String groupName) throws SchedulerException {
289 if (!PropsValues.SCHEDULER_ENABLED) {
290 return;
291 }
292
293 try {
294 Scheduler scheduler = getScheduler(groupName);
295
296 groupName = fixMaxLength(
297 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
298
299 Set<JobKey> jobKeys = scheduler.getJobKeys(
300 GroupMatcher.jobGroupEquals(groupName));
301
302 for (JobKey jobKey : jobKeys) {
303 updateJobState(scheduler, jobKey, TriggerState.NORMAL, false);
304 }
305
306 scheduler.resumeJobs(GroupMatcher.jobGroupEquals(groupName));
307 }
308 catch (Exception e) {
309 throw new SchedulerException(
310 "Unable to resume jobs in group " + groupName, e);
311 }
312 }
313
314 public void resume(String jobName, String groupName)
315 throws SchedulerException {
316
317 if (!PropsValues.SCHEDULER_ENABLED) {
318 return;
319 }
320
321 try {
322 Scheduler scheduler = getScheduler(groupName);
323
324 jobName = fixMaxLength(jobName, JOB_NAME_MAX_LENGTH);
325 groupName = fixMaxLength(
326 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
327
328 JobKey jobKey = new JobKey(jobName, groupName);
329
330 updateJobState(scheduler, jobKey, TriggerState.NORMAL, false);
331
332 scheduler.resumeJob(jobKey);
333 }
334 catch (Exception e) {
335 throw new SchedulerException(
336 "Unable to resume job {jobName=" + jobName + ", groupName=" +
337 groupName + "}",
338 e);
339 }
340 }
341
342 public void schedule(
343 com.liferay.portal.kernel.scheduler.Trigger trigger,
344 String description, String destination, Message message)
345 throws SchedulerException {
346
347 if (!PropsValues.SCHEDULER_ENABLED) {
348 return;
349 }
350
351 try {
352 Scheduler scheduler = getScheduler(trigger.getGroupName());
353
354 StorageType storageType = getStorageType(trigger.getGroupName());
355
356 trigger = TriggerFactoryUtil.buildTrigger(
357 trigger.getTriggerType(), trigger.getJobName(),
358 getOriginalGroupName(trigger.getGroupName()),
359 trigger.getStartDate(), trigger.getEndDate(),
360 trigger.getTriggerContent());
361
362 Trigger quartzTrigger = getQuartzTrigger(trigger);
363
364 if (quartzTrigger == null) {
365 return;
366 }
367
368 description = fixMaxLength(description, DESCRIPTION_MAX_LENGTH);
369
370 if (message == null) {
371 message = new Message();
372 }
373 else {
374 message = message.clone();
375 }
376
377 TriggerKey triggerKey = quartzTrigger.getKey();
378
379 message.put(
380 RECEIVER_KEY,
381 getFullName(triggerKey.getName(), triggerKey.getGroup()));
382
383 schedule(
384 scheduler, storageType, quartzTrigger, description, destination,
385 message);
386 }
387 catch (RuntimeException re) {
388
389
390
391
392 }
393 catch (Exception e) {
394 throw new SchedulerException("Unable to schedule job", e);
395 }
396 }
397
398 public void shutdown() throws SchedulerException {
399 if (!PropsValues.SCHEDULER_ENABLED) {
400 return;
401 }
402
403 try {
404 if (!_persistedScheduler.isShutdown()) {
405 _persistedScheduler.shutdown(false);
406 }
407
408 if (!_memoryScheduler.isShutdown()) {
409 _memoryScheduler.shutdown(false);
410 }
411 }
412 catch (Exception e) {
413 throw new SchedulerException("Unable to shutdown scheduler", e);
414 }
415 }
416
417 public void start() throws SchedulerException {
418 if (!PropsValues.SCHEDULER_ENABLED) {
419 return;
420 }
421
422 try {
423 _persistedScheduler.start();
424
425 initJobState();
426
427 _memoryScheduler.start();
428 }
429 catch (Exception e) {
430 throw new SchedulerException("Unable to start scheduler", e);
431 }
432 }
433
434 public void suppressError(String jobName, String groupName)
435 throws SchedulerException {
436
437 if (!PropsValues.SCHEDULER_ENABLED) {
438 return;
439 }
440
441 try {
442 Scheduler scheduler = getScheduler(groupName);
443
444 jobName = fixMaxLength(jobName, JOB_NAME_MAX_LENGTH);
445 groupName = fixMaxLength(
446 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
447
448 JobKey jobKey = new JobKey(jobName, groupName);
449
450 updateJobState(scheduler, jobKey, null, true);
451 }
452 catch (Exception e) {
453 throw new SchedulerException(
454 "Unable to suppress error for job {jobName=" + jobName +
455 ", groupName=" + groupName + "}",
456 e);
457 }
458 }
459
460 public void unschedule(String groupName) throws SchedulerException {
461 if (!PropsValues.SCHEDULER_ENABLED) {
462 return;
463 }
464
465 try {
466 Scheduler scheduler = getScheduler(groupName);
467
468 groupName = fixMaxLength(
469 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
470
471 Set<JobKey> jobKeys = scheduler.getJobKeys(
472 GroupMatcher.jobGroupEquals(groupName));
473
474 for (JobKey jobKey : jobKeys) {
475 unschedule(scheduler, jobKey);
476 }
477 }
478 catch (Exception e) {
479 throw new SchedulerException(
480 "Unable to unschedule jobs in group " + groupName, e);
481 }
482 }
483
484 public void unschedule(String jobName, String groupName)
485 throws SchedulerException {
486
487 if (!PropsValues.SCHEDULER_ENABLED) {
488 return;
489 }
490
491 try {
492 Scheduler scheduler = getScheduler(groupName);
493
494 jobName = fixMaxLength(jobName, JOB_NAME_MAX_LENGTH);
495 groupName = fixMaxLength(
496 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
497
498 JobKey jobKey = new JobKey(jobName, groupName);
499
500 unschedule(scheduler, jobKey);
501 }
502 catch (Exception e) {
503 throw new SchedulerException(
504 "Unable to unschedule job {jobName=" + jobName +
505 ", groupName=" + groupName + "}",
506 e);
507 }
508 }
509
510 public void update(com.liferay.portal.kernel.scheduler.Trigger trigger)
511 throws SchedulerException {
512
513 if (!PropsValues.SCHEDULER_ENABLED) {
514 return;
515 }
516
517 try {
518 Scheduler scheduler = getScheduler(trigger.getGroupName());
519
520 trigger = TriggerFactoryUtil.buildTrigger(
521 trigger.getTriggerType(), trigger.getJobName(),
522 getOriginalGroupName(trigger.getGroupName()),
523 trigger.getStartDate(), trigger.getEndDate(),
524 trigger.getTriggerContent());
525
526 update(scheduler, trigger);
527 }
528 catch (Exception e) {
529 throw new SchedulerException("Unable to update trigger", e);
530 }
531 }
532
533 protected String fixMaxLength(String argument, int maxLength) {
534 if (argument == null) {
535 return null;
536 }
537
538 if (argument.length() > maxLength) {
539 argument = argument.substring(0, maxLength);
540 }
541
542 return argument;
543 }
544
545 protected String getFullName(String jobName, String groupName) {
546 return groupName.concat(StringPool.PERIOD).concat(jobName);
547 }
548
549 protected JobState getJobState(JobDataMap jobDataMap) {
550 Map<String, Object> jobStateMap = (Map<String, Object>)jobDataMap.get(
551 JOB_STATE);
552
553 return JobStateSerializeUtil.deserialize(jobStateMap);
554 }
555
556 protected Message getMessage(JobDataMap jobDataMap) {
557 String messageJSON = (String)jobDataMap.get(MESSAGE);
558
559 return (Message)JSONFactoryUtil.deserialize(messageJSON);
560 }
561
562 protected String getOriginalGroupName(String groupName) {
563 int pos = groupName.indexOf(CharPool.POUND);
564
565 return groupName.substring(pos + 1);
566 }
567
568 protected Trigger getQuartzTrigger(
569 com.liferay.portal.kernel.scheduler.Trigger trigger)
570 throws SchedulerException {
571
572 if (trigger == null) {
573 return null;
574 }
575
576 Date endDate = trigger.getEndDate();
577 String jobName = fixMaxLength(
578 trigger.getJobName(), JOB_NAME_MAX_LENGTH);
579 String groupName = fixMaxLength(
580 trigger.getGroupName(), GROUP_NAME_MAX_LENGTH);
581
582 Date startDate = trigger.getStartDate();
583
584 if (startDate == null) {
585 if (ServerDetector.isTomcat()) {
586 startDate = new Date(System.currentTimeMillis() + Time.MINUTE);
587 }
588 else {
589 startDate = new Date(
590 System.currentTimeMillis() + Time.MINUTE * 3);
591 }
592 }
593
594 Trigger quartzTrigger = null;
595
596 TriggerType triggerType = trigger.getTriggerType();
597
598 if (triggerType.equals(TriggerType.CRON)) {
599 try {
600 TriggerBuilder<Trigger>triggerBuilder =
601 TriggerBuilder.newTrigger();
602
603 triggerBuilder.endAt(endDate);
604 triggerBuilder.forJob(jobName, groupName);
605 triggerBuilder.startAt(startDate);
606 triggerBuilder.withIdentity(jobName, groupName);
607
608 CronScheduleBuilder cronScheduleBuilder =
609 CronScheduleBuilder.cronSchedule(
610 (String)trigger.getTriggerContent());
611
612 triggerBuilder.withSchedule(cronScheduleBuilder);
613
614 quartzTrigger = triggerBuilder.build();
615 }
616 catch (ParseException pe) {
617 throw new SchedulerException(
618 "Unable to parse cron text " + trigger.getTriggerContent());
619 }
620 }
621 else if (triggerType.equals(TriggerType.SIMPLE)) {
622 long interval = (Long)trigger.getTriggerContent();
623
624 if (interval <= 0) {
625 if (_log.isDebugEnabled()) {
626 _log.debug(
627 "Not scheduling " + trigger.getJobName() +
628 " because interval is less than or equal to 0");
629 }
630
631 return null;
632 }
633
634 TriggerBuilder<Trigger>triggerBuilder = TriggerBuilder.newTrigger();
635
636 triggerBuilder.endAt(endDate);
637 triggerBuilder.forJob(jobName, groupName);
638 triggerBuilder.startAt(startDate);
639 triggerBuilder.withIdentity(jobName, groupName);
640
641 SimpleScheduleBuilder simpleScheduleBuilder =
642 SimpleScheduleBuilder.simpleSchedule();
643
644 simpleScheduleBuilder.withIntervalInMilliseconds(interval);
645 simpleScheduleBuilder.withRepeatCount(
646 SimpleTrigger.REPEAT_INDEFINITELY);
647
648 triggerBuilder.withSchedule(simpleScheduleBuilder);
649
650 quartzTrigger = triggerBuilder.build();
651 }
652 else {
653 throw new SchedulerException(
654 "Unknown trigger type " + trigger.getTriggerType());
655 }
656
657 return quartzTrigger;
658 }
659
660 protected SchedulerResponse getScheduledJob(
661 Scheduler scheduler, JobKey jobKey)
662 throws Exception {
663
664 JobDetail jobDetail = scheduler.getJobDetail(jobKey);
665
666 if (jobDetail == null) {
667 return null;
668 }
669
670 JobDataMap jobDataMap = jobDetail.getJobDataMap();
671
672 String description = jobDataMap.getString(DESCRIPTION);
673 String destinationName = jobDataMap.getString(DESTINATION_NAME);
674 Message message = getMessage(jobDataMap);
675 StorageType storageType = StorageType.valueOf(
676 jobDataMap.getString(STORAGE_TYPE));
677
678 SchedulerResponse schedulerResponse = null;
679
680 String jobName = jobKey.getName();
681 String groupName = jobKey.getGroup();
682
683 TriggerKey triggerKey = new TriggerKey(jobName, groupName);
684
685 Trigger trigger = scheduler.getTrigger(triggerKey);
686
687 JobState jobState = getJobState(jobDataMap);
688
689 message.put(JOB_STATE, jobState);
690
691 if (trigger == null) {
692 schedulerResponse = new SchedulerResponse();
693
694 schedulerResponse.setDescription(description);
695 schedulerResponse.setDestinationName(destinationName);
696 schedulerResponse.setGroupName(groupName);
697 schedulerResponse.setJobName(jobName);
698 schedulerResponse.setMessage(message);
699 schedulerResponse.setStorageType(storageType);
700 }
701 else {
702 message.put(END_TIME, trigger.getEndTime());
703 message.put(FINAL_FIRE_TIME, trigger.getFinalFireTime());
704 message.put(NEXT_FIRE_TIME, trigger.getNextFireTime());
705 message.put(PREVIOUS_FIRE_TIME, trigger.getPreviousFireTime());
706 message.put(START_TIME, trigger.getStartTime());
707
708 if (CronTrigger.class.isAssignableFrom(trigger.getClass())) {
709
710 CronTrigger cronTrigger = CronTrigger.class.cast(trigger);
711
712 schedulerResponse = new SchedulerResponse();
713
714 schedulerResponse.setDescription(description);
715 schedulerResponse.setDestinationName(destinationName);
716 schedulerResponse.setMessage(message);
717 schedulerResponse.setStorageType(storageType);
718 schedulerResponse.setTrigger(
719 new com.liferay.portal.kernel.scheduler.CronTrigger(
720 jobName, groupName, cronTrigger.getStartTime(),
721 cronTrigger.getEndTime(),
722 cronTrigger.getCronExpression()));
723 }
724 else if (SimpleTrigger.class.isAssignableFrom(trigger.getClass())) {
725 SimpleTrigger simpleTrigger = SimpleTrigger.class.cast(trigger);
726
727 schedulerResponse = new SchedulerResponse();
728
729 schedulerResponse.setDescription(description);
730 schedulerResponse.setDestinationName(destinationName);
731 schedulerResponse.setMessage(message);
732 schedulerResponse.setStorageType(storageType);
733 schedulerResponse.setTrigger(
734 new IntervalTrigger(
735 jobName, groupName, simpleTrigger.getStartTime(),
736 simpleTrigger.getEndTime(),
737 simpleTrigger.getRepeatInterval()));
738 }
739 }
740
741 return schedulerResponse;
742 }
743
744 protected List<SchedulerResponse> getScheduledJobs(
745 Scheduler scheduler, String groupName)
746 throws Exception {
747
748 groupName = fixMaxLength(
749 getOriginalGroupName(groupName), GROUP_NAME_MAX_LENGTH);
750
751 List<SchedulerResponse> schedulerResponses =
752 new ArrayList<SchedulerResponse>();
753
754 Set<JobKey> jobKeys = scheduler.getJobKeys(
755 GroupMatcher.jobGroupEquals(groupName));
756
757 for (JobKey jobKey : jobKeys) {
758 SchedulerResponse schedulerResponse = getScheduledJob(
759 scheduler, jobKey);
760
761 if (schedulerResponse != null) {
762 schedulerResponses.add(schedulerResponse);
763 }
764 }
765
766 return schedulerResponses;
767 }
768
769 protected Scheduler getScheduler(String groupName) throws Exception {
770 if (groupName.startsWith(StorageType.PERSISTED.toString())) {
771 return _persistedScheduler;
772 }
773 else {
774 return _memoryScheduler;
775 }
776 }
777
778 protected StorageType getStorageType(String groupName) {
779 int pos = groupName.indexOf(CharPool.POUND);
780
781 String storageTypeString = groupName.substring(0, pos);
782
783 return StorageType.valueOf(storageTypeString);
784 }
785
786 protected Scheduler initializeScheduler(
787 String propertiesPrefix, boolean useQuartzCluster)
788 throws Exception {
789
790 StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
791
792 Properties properties = PropsUtil.getProperties(propertiesPrefix, true);
793
794 if (useQuartzCluster) {
795 DB db = DBFactoryUtil.getDB();
796
797 String dbType = db.getType();
798
799 if (dbType.equals(DB.TYPE_SQLSERVER)) {
800 properties.setProperty(
801 "org.quartz.jobStore.lockHandler.class",
802 UpdateLockRowSemaphore.class.getName());
803 }
804
805 if (PropsValues.CLUSTER_LINK_ENABLED) {
806 if (dbType.equals(DB.TYPE_HYPERSONIC)) {
807 _log.error("Unable to cluster scheduler on Hypersonic");
808 }
809 else {
810 properties.put(
811 "org.quartz.jobStore.isClustered",
812 Boolean.TRUE.toString());
813 }
814 }
815 }
816
817 schedulerFactory.initialize(properties);
818
819 return schedulerFactory.getScheduler();
820 }
821
822 protected void initJobState() throws Exception {
823 List<String> groupNames = _persistedScheduler.getJobGroupNames();
824
825 for (String groupName : groupNames) {
826 Set<JobKey> jobkeys = _persistedScheduler.getJobKeys(
827 GroupMatcher.jobGroupEquals(groupName));
828
829 for (JobKey jobKey : jobkeys) {
830 Trigger trigger = _persistedScheduler.getTrigger(
831 new TriggerKey(jobKey.getName(), jobKey.getGroup()));
832
833 if (trigger != null) {
834 continue;
835 }
836
837 JobDetail jobDetail = _persistedScheduler.getJobDetail(jobKey);
838
839 JobDataMap jobDataMap = jobDetail.getJobDataMap();
840
841 JobState jobState = getJobState(jobDataMap);
842
843 jobState.setTriggerState(TriggerState.COMPLETE);
844
845 jobDataMap.put(
846 JOB_STATE, JobStateSerializeUtil.serialize(jobState));
847
848 _persistedScheduler.addJob(jobDetail, true);
849 }
850 }
851 }
852
853 protected void schedule(
854 Scheduler scheduler, StorageType storageType, Trigger trigger,
855 String description, String destinationName, Message message)
856 throws Exception {
857
858 try {
859 JobDetail jobDetail = null;
860
861 JobBuilder jobBuilder = JobBuilder.newJob(MessageSenderJob.class);
862
863 jobBuilder.storeDurably(scheduler == _persistedScheduler);
864 jobBuilder.withIdentity(trigger.getJobKey());
865
866 jobDetail = jobBuilder.build();
867
868 JobDataMap jobDataMap = jobDetail.getJobDataMap();
869
870 jobDataMap.put(DESCRIPTION, description);
871 jobDataMap.put(DESTINATION_NAME, destinationName);
872 jobDataMap.put(MESSAGE, JSONFactoryUtil.serialize(message));
873 jobDataMap.put(STORAGE_TYPE, storageType.toString());
874
875 JobState jobState = new JobState(
876 TriggerState.NORMAL, message.getInteger(EXCEPTIONS_MAX_SIZE));
877
878 jobDataMap.put(
879 JOB_STATE, JobStateSerializeUtil.serialize(jobState));
880
881 synchronized (this) {
882 scheduler.deleteJob(trigger.getJobKey());
883 scheduler.scheduleJob(jobDetail, trigger);
884 }
885 }
886 catch (ObjectAlreadyExistsException oare) {
887 if (_log.isInfoEnabled()) {
888 _log.info("Message is already scheduled");
889 }
890 }
891 }
892
893 protected void unschedule(Scheduler scheduler, JobKey jobKey)
894 throws Exception {
895
896 JobDetail jobDetail = scheduler.getJobDetail(jobKey);
897
898 TriggerKey triggerKey = new TriggerKey(
899 jobKey.getName(), jobKey.getGroup());
900
901 if (jobDetail == null) {
902 return;
903 }
904
905 if (scheduler == _memoryScheduler) {
906 scheduler.unscheduleJob(triggerKey);
907
908 return;
909 }
910
911 JobDataMap jobDataMap = jobDetail.getJobDataMap();
912
913 JobState jobState = getJobState(jobDataMap);
914
915 Trigger trigger = scheduler.getTrigger(triggerKey);
916
917 jobState.setTriggerDate(END_TIME, new Date());
918 jobState.setTriggerDate(FINAL_FIRE_TIME, trigger.getPreviousFireTime());
919 jobState.setTriggerDate(NEXT_FIRE_TIME, null);
920 jobState.setTriggerDate(
921 PREVIOUS_FIRE_TIME, trigger.getPreviousFireTime());
922 jobState.setTriggerDate(START_TIME, trigger.getStartTime());
923
924 jobState.setTriggerState(TriggerState.UNSCHEDULED);
925
926 jobState.clearExceptions();
927
928 jobDataMap.put(JOB_STATE, JobStateSerializeUtil.serialize(jobState));
929
930 scheduler.unscheduleJob(triggerKey);
931
932 scheduler.addJob(jobDetail, true);
933 }
934
935 protected void update(
936 Scheduler scheduler,
937 com.liferay.portal.kernel.scheduler.Trigger trigger)
938 throws Exception {
939
940 Trigger quartzTrigger = getQuartzTrigger(trigger);
941
942 if (quartzTrigger == null) {
943 return;
944 }
945
946 TriggerKey triggerKey = quartzTrigger.getKey();
947
948 if (scheduler.getTrigger(triggerKey) != null) {
949 scheduler.rescheduleJob(triggerKey, quartzTrigger);
950 }
951 else {
952 JobKey jobKey = quartzTrigger.getJobKey();
953
954 JobDetail jobDetail = scheduler.getJobDetail(jobKey);
955
956 if (jobDetail == null) {
957 return;
958 }
959
960 updateJobState(scheduler, jobKey, TriggerState.NORMAL, true);
961
962 synchronized (this) {
963 scheduler.deleteJob(jobKey);
964 scheduler.scheduleJob(jobDetail, quartzTrigger);
965 }
966 }
967 }
968
969 protected void updateJobState(
970 Scheduler scheduler, JobKey jobKey, TriggerState triggerState,
971 boolean suppressError)
972 throws Exception {
973
974 JobDetail jobDetail = scheduler.getJobDetail(jobKey);
975
976 JobDataMap jobDataMap = jobDetail.getJobDataMap();
977
978 JobState jobState = getJobState(jobDataMap);
979
980 if (triggerState != null) {
981 jobState.setTriggerState(triggerState);
982 }
983
984 if (suppressError) {
985 jobState.clearExceptions();
986 }
987
988 jobDataMap.put(JOB_STATE, JobStateSerializeUtil.serialize(jobState));
989
990 scheduler.addJob(jobDetail, true);
991 }
992
993 @BeanReference(name = "com.liferay.portal.service.QuartzLocalService")
994 protected QuartzLocalService quartzLocalService;
995
996 private static Log _log = LogFactoryUtil.getLog(
997 QuartzSchedulerEngine.class);
998
999 private Scheduler _memoryScheduler;
1000 private Scheduler _persistedScheduler;
1001
1002 }