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