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.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    /**
071     * @author Michael C. Han
072     * @author Bruno Farache
073     * @author Shuyang Zhou
074     * @author Wesley Gong
075     * @author Tina Tian
076     */
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                            // ServerDetector will throw an exception when JobSchedulerImpl is
390                            // initialized in a test environment
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    }