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