KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > scheduler > CmsScheduleManager


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/scheduler/CmsScheduleManager.java,v $
3  * Date : $Date: 2006/03/27 14:52:20 $
4  * Version: $Revision: 1.27 $
5  *
6  * This library is part of OpenCms -
7  * the Open Source Content Mananagement System
8  *
9  * Copyright (c) 2005 Alkacon Software GmbH (http://www.alkacon.com)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * For further information about Alkacon Software GmbH, please see the
22  * company website: http://www.alkacon.com
23  *
24  * For further information about OpenCms, please see the
25  * project website: http://www.opencms.org
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30  */

31
32 package org.opencms.scheduler;
33
34 import org.opencms.file.CmsObject;
35 import org.opencms.i18n.CmsMessageContainer;
36 import org.opencms.main.CmsLog;
37 import org.opencms.main.OpenCms;
38 import org.opencms.security.CmsRole;
39 import org.opencms.security.CmsRoleViolationException;
40 import org.opencms.util.CmsStringUtil;
41 import org.opencms.util.CmsUUID;
42
43 import java.text.ParseException JavaDoc;
44 import java.util.ArrayList JavaDoc;
45 import java.util.Collections JavaDoc;
46 import java.util.Date JavaDoc;
47 import java.util.Iterator JavaDoc;
48 import java.util.List JavaDoc;
49 import java.util.Properties JavaDoc;
50
51 import org.apache.commons.logging.Log;
52
53 import org.quartz.CronTrigger;
54 import org.quartz.Job;
55 import org.quartz.JobDataMap;
56 import org.quartz.JobDetail;
57 import org.quartz.JobExecutionContext;
58 import org.quartz.Scheduler;
59 import org.quartz.SchedulerException;
60 import org.quartz.SchedulerFactory;
61 import org.quartz.impl.StdSchedulerFactory;
62
63 /**
64  * Manages the OpenCms scheduled jobs.<p>
65  *
66  * Please see the documentation of the class {@link org.opencms.scheduler.CmsScheduledJobInfo}
67  * for a full description of the OpenCms scheduling capabilities.<p>
68  *
69  * The OpenCms scheduler implementation internally uses the
70  * <a HREF="http://www.opensymphony.com/quartz/">Quartz scheduler</a> from
71  * the <a HREF="http://www.opensymphony.com/">OpenSymphony project</a>.<p>
72  *
73  * This manager class implements the <code>org.quartz.Job</code> interface
74  * and wraps all calls to the {@link org.opencms.scheduler.I_CmsScheduledJob} implementing
75  * classes.<p>
76  *
77  * @author Alexander Kandzior
78  *
79  * @version $Revision: 1.27 $
80  *
81  * @since 6.0.0
82  *
83  * @see org.opencms.scheduler.CmsScheduledJobInfo
84  */

85 public class CmsScheduleManager implements Job {
86
87     /** Key for the scheduled job description in the job data map. */
88     public static final String JavaDoc SCHEDULER_JOB_INFO = "org.opencms.scheduler.CmsScheduledJobInfo";
89
90     /** The log object for this class. */
91     private static final Log LOG = CmsLog.getLog(CmsScheduleManager.class);
92
93     /** The Admin context used for creation of users for the individual jobs. */
94     private static CmsObject m_adminCms;
95
96     /** The list of job entries from the configuration. */
97     private List JavaDoc m_configuredJobs;
98
99     /** The list of scheduled jobs. */
100     private List JavaDoc m_jobs;
101
102     /** The initialized scheduler. */
103     private Scheduler m_scheduler;
104
105     /**
106      * Default constructor for the scheduler manager,
107      * used only when a new job is scheduled.<p>
108      */

109     public CmsScheduleManager() {
110
111         // important: this constructor is always called when a new job is
112
// generated, so it _must_ remain empty
113
}
114
115     /**
116      * Used by the configuration to create a new Scheduler during system startup.<p>
117      *
118      * @param configuredJobs the jobs from the configuration
119      */

120     public CmsScheduleManager(List JavaDoc configuredJobs) {
121
122         m_configuredJobs = configuredJobs;
123         int size = 0;
124         if (m_configuredJobs != null) {
125             size = m_configuredJobs.size();
126         }
127
128         if (CmsLog.INIT.isInfoEnabled()) {
129             CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SCHEDULER_CREATED_1, new Integer JavaDoc(size)));
130         }
131     }
132
133     /**
134      * Implementation of the Quartz job interface.<p>
135      *
136      * The architecture is that this scheduler manager generates
137      * a new (empty) instance of itself for every OpenCms job scheduled with Quartz.
138      * When the Quartz job is executed, the configured
139      * implementaion of {@link I_CmsScheduledJob} will be called from this method.<p>
140      *
141      * @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
142      */

143     public void execute(JobExecutionContext context) {
144
145         JobDataMap jobData = context.getJobDetail().getJobDataMap();
146         CmsScheduledJobInfo jobInfo = (CmsScheduledJobInfo)jobData.get(SCHEDULER_JOB_INFO);
147
148         if (jobInfo == null) {
149             LOG.error(Messages.get().getBundle().key(Messages.LOG_INVALID_JOB_1, context.getJobDetail().getFullName()));
150             // can not continue
151
return;
152         }
153
154         if (LOG.isDebugEnabled()) {
155             LOG.debug(Messages.get().getBundle().key(Messages.LOG_JOB_STARTING_1, jobInfo.getJobName()));
156         }
157
158         I_CmsScheduledJob job = jobInfo.getJobInstance();
159
160         if (job != null) {
161             try {
162                 // launch the job
163
CmsObject cms = null;
164
165                 // only simple test cases might not have admin cms available
166
if (m_adminCms != null) {
167                     // generate a CmsObject for the job context
168
cms = OpenCms.initCmsObject(m_adminCms, jobInfo.getContextInfo());
169                 }
170
171                 String JavaDoc result = job.launch(cms, jobInfo.getParameters());
172                 if (CmsStringUtil.isNotEmpty(result) && LOG.isInfoEnabled()) {
173                     LOG.info(Messages.get().getBundle().key(
174                         Messages.LOG_JOB_EXECUTION_OK_2,
175                         jobInfo.getJobName(),
176                         result));
177                 }
178             } catch (Throwable JavaDoc t) {
179                 LOG.error(Messages.get().getBundle().key(Messages.LOG_JOB_EXECUTION_ERROR_1, jobInfo.getJobName()), t);
180             }
181         }
182
183         if (LOG.isDebugEnabled()) {
184             LOG.debug(Messages.get().getBundle().key(Messages.LOG_JOB_EXECUTED_1, jobInfo.getJobName()));
185             Date JavaDoc nextExecution = jobInfo.getExecutionTimeNext();
186             if (nextExecution != null) {
187                 LOG.info(Messages.get().getBundle().key(
188                     Messages.LOG_JOB_NEXT_EXECUTION_2,
189                     jobInfo.getJobName(),
190                     nextExecution));
191             }
192         }
193     }
194
195     /**
196      * Returns the currently scheduled job description identified by the given id.
197      *
198      * @param id the job id
199      *
200      * @return a job or <code>null</code> if not found
201      */

202     public CmsScheduledJobInfo getJob(String JavaDoc id) {
203
204         Iterator JavaDoc it = m_jobs.iterator();
205         while (it.hasNext()) {
206             CmsScheduledJobInfo job = (CmsScheduledJobInfo)it.next();
207             if (job.getId().equals(id)) {
208                 return job;
209             }
210         }
211         // not found
212
return null;
213     }
214
215     /**
216      * Returns the currently scheduled job descriptions in an unmodifiable list.<p>
217      *
218      * The objects in the List are of type <code>{@link CmsScheduledJobInfo}</code>.<p>
219      *
220      * @return the currently scheduled job descriptions in an unmodifiable list
221      */

222     public List JavaDoc getJobs() {
223
224         return Collections.unmodifiableList(m_jobs);
225     }
226
227     /**
228      * Initializes the OpenCms scheduler.<p>
229      *
230      * @param cms an OpenCms context object that must have been initialized with "Admin" permissions
231      *
232      * @throws CmsRoleViolationException if the user has insufficient role permissions
233      */

234     public synchronized void initialize(CmsObject cms) throws CmsRoleViolationException {
235
236         if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) {
237             // simple unit tests will have runlevel 1 and no CmsObject
238
cms.checkRole(CmsRole.SCHEDULER_MANAGER);
239         }
240
241         // the list of job entries
242
m_jobs = new ArrayList JavaDoc();
243
244         // save the admin cms
245
m_adminCms = cms;
246
247         // Quartz scheduler settings
248
Properties JavaDoc properties = new Properties JavaDoc();
249         properties.put(StdSchedulerFactory.PROP_SCHED_INSTANCE_NAME, "OpenCmsScheduler");
250         properties.put(StdSchedulerFactory.PROP_SCHED_THREAD_NAME, "OpenCms: Scheduler");
251         properties.put(StdSchedulerFactory.PROP_SCHED_RMI_EXPORT, CmsStringUtil.FALSE);
252         properties.put(StdSchedulerFactory.PROP_SCHED_RMI_PROXY, CmsStringUtil.FALSE);
253         properties.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, CmsSchedulerThreadPool.class.getName());
254         properties.put(StdSchedulerFactory.PROP_JOB_STORE_CLASS, "org.quartz.simpl.RAMJobStore");
255
256         try {
257             // initilize the Quartz scheduler
258
SchedulerFactory schedulerFactory = new StdSchedulerFactory(properties);
259             m_scheduler = schedulerFactory.getScheduler();
260         } catch (Exception JavaDoc e) {
261             LOG.error(Messages.get().getBundle().key(Messages.LOG_NO_SCHEDULER_0), e);
262             // can not continue
263
m_scheduler = null;
264             return;
265         }
266
267         if (CmsLog.INIT.isInfoEnabled()) {
268             CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SCHEDULER_INITIALIZED_0));
269         }
270
271         if (m_configuredJobs != null) {
272             // add all jobs from the system configuration
273
for (int i = 0; i < m_configuredJobs.size(); i++) {
274                 try {
275                     CmsScheduledJobInfo job = (CmsScheduledJobInfo)m_configuredJobs.get(i);
276                     scheduleJob(cms, job);
277                 } catch (CmsSchedulerException e) {
278                     // ignore this job, but keep scheduling the other jobs
279
// note: the log is has already been written
280
}
281             }
282         }
283
284         try {
285             // start the scheduler
286
m_scheduler.start();
287         } catch (Exception JavaDoc e) {
288             LOG.error(Messages.get().getBundle().key(Messages.LOG_CANNOT_START_SCHEDULER_0), e);
289             // can not continue
290
m_scheduler = null;
291             return;
292         }
293
294         if (CmsLog.INIT.isInfoEnabled()) {
295             CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SCHEDULER_STARTED_0));
296             CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SCHEDULER_CONFIG_FINISHED_0));
297         }
298     }
299
300     /**
301      * Adds a new job to the scheduler.<p>
302      *
303      * @param cms an OpenCms context object that must have been initialized with "Admin" permissions
304      * @param jobInfo the job info describing the job to schedule
305      *
306      * @throws CmsRoleViolationException if the user has insufficient role permissions
307      * @throws CmsSchedulerException if the job could not be scheduled for any reason
308      */

309     public synchronized void scheduleJob(CmsObject cms, CmsScheduledJobInfo jobInfo)
310     throws CmsRoleViolationException, CmsSchedulerException {
311
312         if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) {
313             // simple unit tests will have runlevel 1 and no CmsObject
314
cms.checkRole(CmsRole.SCHEDULER_MANAGER);
315         }
316
317         if ((jobInfo == null) || (jobInfo.getClassName() == null)) {
318             // prevent NPE
319
CmsMessageContainer message = Messages.get().container(Messages.ERR_INVALID_JOB_CONFIGURATION_0);
320             LOG.error(message.key());
321             // can not continue
322
throw new CmsSchedulerException(message);
323         }
324
325         if (m_scheduler == null) {
326             CmsMessageContainer message = Messages.get().container(Messages.ERR_NO_SCHEDULER_1, jobInfo.getJobName());
327             LOG.error(message.key());
328             // can not continue
329
throw new CmsSchedulerException(message);
330         }
331
332         Class JavaDoc jobClass;
333         try {
334             jobClass = Class.forName(jobInfo.getClassName());
335         } catch (ClassNotFoundException JavaDoc e) {
336             // class does not exist
337
CmsMessageContainer message = Messages.get().container(
338                 Messages.ERR_JOB_CLASS_NOT_FOUND_1,
339                 jobInfo.getClassName());
340             LOG.error(message.key());
341             throw new CmsSchedulerException(message);
342         }
343         if (!I_CmsScheduledJob.class.isAssignableFrom(jobClass)) {
344             // class does not implement required interface
345
CmsMessageContainer message = Messages.get().container(
346                 Messages.ERR_JOB_CLASS_BAD_INTERFACE_2,
347                 jobInfo.getClassName(),
348                 I_CmsScheduledJob.class.getName());
349             LOG.error(message.key());
350             throw new CmsSchedulerException(message);
351         }
352
353         String JavaDoc jobId = jobInfo.getId();
354         boolean idCreated = false;
355         if (jobId == null) {
356             // generate a new job id
357
CmsUUID jobUUID = new CmsUUID();
358             jobId = "OpenCmsJob_".concat(jobUUID.toString());
359             jobInfo.setId(jobId);
360             idCreated = true;
361         }
362
363         // generate Quartz job trigger
364
CronTrigger trigger = new CronTrigger(jobId, Scheduler.DEFAULT_GROUP);
365
366         try {
367             trigger.setCronExpression(jobInfo.getCronExpression());
368         } catch (ParseException JavaDoc e) {
369             if (idCreated) {
370                 jobInfo.setId(null);
371             }
372             CmsMessageContainer message = Messages.get().container(
373                 Messages.ERR_BAD_CRON_EXPRESSION_2,
374                 jobInfo.getJobName(),
375                 jobInfo.getCronExpression());
376             LOG.error(message.key());
377             // can not continue
378
throw new CmsSchedulerException(message);
379         }
380
381         CmsScheduledJobInfo oldJob = null;
382         if (!idCreated) {
383             // this job is already scheduled, remove the currently scheduled instance and keep the id
384
// important: since the new job may have errors, it's required to make sure the old job is only unscheduled
385
// if the new job info is o.k.
386
oldJob = unscheduleJob(cms, jobId);
387             if (oldJob == null) {
388                 CmsMessageContainer message = Messages.get().container(Messages.ERR_JOB_WITH_ID_DOES_NOT_EXIST_1, jobId);
389                 LOG.warn(message.key());
390                 // can not continue
391
throw new CmsSchedulerException(message);
392             }
393             // open the job configuration (in case it has been frozen)
394
jobInfo.setFrozen(false);
395         }
396
397         // only schedule jobs when they are marked as active
398
if (jobInfo.isActive()) {
399
400             // generate Quartz job detail
401
JobDetail jobDetail = new JobDetail(jobInfo.getId(), Scheduler.DEFAULT_GROUP, CmsScheduleManager.class);
402
403             // add the trigger to the job info
404
jobInfo.setTrigger(trigger);
405
406             // now set the job data
407
JobDataMap jobData = new JobDataMap();
408             jobData.put(CmsScheduleManager.SCHEDULER_JOB_INFO, jobInfo);
409             jobDetail.setJobDataMap(jobData);
410
411             // finally add the job to the Quartz scheduler
412
try {
413                 m_scheduler.scheduleJob(jobDetail, trigger);
414             } catch (Exception JavaDoc e) {
415                 if (LOG.isDebugEnabled()) {
416                     LOG.debug(e.getMessage(), e);
417                 }
418                 if (idCreated) {
419                     jobInfo.setId(null);
420                 }
421                 CmsMessageContainer message = Messages.get().container(
422                     Messages.ERR_COULD_NOT_SCHEDULE_JOB_2,
423                     jobInfo.getJobName(),
424                     jobInfo.getClassName());
425                 if (oldJob != null) {
426                     // make sure an old job is re-scheduled
427
jobDetail = new JobDetail(oldJob.getId(), Scheduler.DEFAULT_GROUP, CmsScheduleManager.class);
428                     jobDetail.setJobDataMap(jobData);
429                     try {
430                         m_scheduler.scheduleJob(jobDetail, oldJob.getTrigger());
431                         m_jobs.add(oldJob);
432                     } catch (SchedulerException e2) {
433                         if (LOG.isDebugEnabled()) {
434                             LOG.debug(e2.getMessage(), e2);
435                         }
436                         // unable to re-schedule original job - not much we can do about this...
437
message = Messages.get().container(
438                             Messages.ERR_COULD_NOT_RESCHEDULE_JOB_2,
439                             jobInfo.getJobName(),
440                             jobInfo.getClassName());
441                     }
442                 }
443                 if (LOG.isWarnEnabled()) {
444                     LOG.warn(message.key());
445                 }
446                 throw new CmsSchedulerException(message);
447             }
448         }
449
450         // freeze the scheduled job configuration
451
jobInfo.initConfiguration();
452
453         // add the job to the list of configured jobs
454
m_jobs.add(jobInfo);
455
456         if (LOG.isInfoEnabled()) {
457             LOG.info(Messages.get().getBundle().key(
458                 Messages.LOG_JOB_SCHEDULED_4,
459                 new Object JavaDoc[] {
460                     new Integer JavaDoc(m_jobs.size()),
461                     jobInfo.getJobName(),
462                     jobInfo.getClassName(),
463                     jobInfo.getContextInfo().getUserName()}));
464             Date JavaDoc nextExecution = jobInfo.getExecutionTimeNext();
465             if (nextExecution != null) {
466                 LOG.info(Messages.get().getBundle().key(
467                     Messages.LOG_JOB_NEXT_EXECUTION_2,
468                     jobInfo.getJobName(),
469                     nextExecution));
470             }
471         }
472     }
473
474     /**
475      * Shuts down this instance of the OpenCms scheduler manager.<p>
476      */

477     public synchronized void shutDown() {
478
479         m_adminCms = null;
480
481         if (CmsLog.INIT.isInfoEnabled()) {
482             CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_1, this.getClass().getName()));
483         }
484
485         if (m_scheduler != null) {
486             try {
487                 m_scheduler.shutdown();
488             } catch (SchedulerException e) {
489                 LOG.error(Messages.get().getBundle().key(Messages.LOG_SHUTDOWN_ERROR_0));
490             }
491         }
492
493         m_scheduler = null;
494     }
495
496     /**
497      * Removes a currently scheduled job from the scheduler.<p>
498      *
499      * @param cms an OpenCms context object that must have been initialized with "Admin" permissions
500      * @param jobId the id of the job to unschedule, obtained with <code>{@link CmsScheduledJobInfo#getId()}</code>
501      *
502      * @return the <code>{@link CmsScheduledJobInfo}</code> of the sucessfully unscheduled job,
503      * or <code>null</code> if the job could not be unscheduled
504      *
505      * @throws CmsRoleViolationException if the user has insufficient role permissions
506      */

507     public synchronized CmsScheduledJobInfo unscheduleJob(CmsObject cms, String JavaDoc jobId) throws CmsRoleViolationException {
508
509         if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) {
510             // simple unit tests will have runlevel 1 and no CmsObject
511
cms.checkRole(CmsRole.SCHEDULER_MANAGER);
512         }
513
514         CmsScheduledJobInfo jobInfo = null;
515         if (m_jobs.size() > 0) {
516             // try to remove the job from the OpenCms list of jobs
517
for (int i = (m_jobs.size() - 1); i >= 0; i--) {
518                 CmsScheduledJobInfo job = (CmsScheduledJobInfo)m_jobs.get(i);
519                 if (jobId.equals(job.getId())) {
520                     m_jobs.remove(i);
521                     if (jobInfo != null) {
522                         LOG.error(Messages.get().getBundle().key(Messages.LOG_MULTIPLE_JOBS_FOUND_1, jobId));
523                     }
524                     jobInfo = job;
525                 }
526             }
527         }
528
529         if ((jobInfo != null) && jobInfo.isActive()) {
530             // job currently active, remove it from the Quartz scheduler
531
try {
532                 // try to remove the job from Quartz
533
m_scheduler.unscheduleJob(jobId, Scheduler.DEFAULT_GROUP);
534                 if (LOG.isDebugEnabled()) {
535                     LOG.debug(Messages.get().getBundle().key(Messages.LOG_UNSCHEDULED_JOB_1, jobId));
536                 }
537             } catch (SchedulerException e) {
538                 if (LOG.isDebugEnabled()) {
539                     LOG.debug(Messages.get().getBundle().key(Messages.LOG_UNSCHEDULING_ERROR_1, jobId));
540                 }
541             }
542         }
543
544         return jobInfo;
545     }
546 }
Popular Tags