KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > lenya > cms > scheduler > SchedulerWrapper


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */

17
18 /* $Id: SchedulerWrapper.java 42598 2004-03-01 16:18:28Z gregor $ */
19
20 package org.apache.lenya.cms.scheduler;
21
22 import java.io.File JavaDoc;
23 import java.text.DateFormat JavaDoc;
24 import java.text.SimpleDateFormat JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Date JavaDoc;
27 import java.util.GregorianCalendar JavaDoc;
28 import java.util.List JavaDoc;
29
30 import javax.servlet.http.HttpServletRequest JavaDoc;
31
32 import org.apache.avalon.framework.configuration.Configuration;
33 import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
34 import org.apache.lenya.cms.publication.Publication;
35 import org.apache.lenya.cms.publication.PublicationException;
36 import org.apache.lenya.cms.publication.PublicationFactory;
37 import org.apache.lenya.cms.scheduler.xml.TriggerHelper;
38 import org.apache.lenya.xml.NamespaceHelper;
39 import org.apache.log4j.Category;
40 import org.quartz.JobDataMap;
41 import org.quartz.JobDetail;
42 import org.quartz.Scheduler;
43 import org.quartz.SchedulerException;
44 import org.quartz.SchedulerFactory;
45 import org.quartz.Trigger;
46 import org.quartz.impl.StdSchedulerFactory;
47 import org.w3c.dom.Document JavaDoc;
48 import org.w3c.dom.Element JavaDoc;
49
50 public class SchedulerWrapper {
51
52     private static Category log = Category.getInstance(SchedulerWrapper.class);
53     public static final String JavaDoc JOB_PREFIX = "job";
54     public static final String JavaDoc JOB_ID = "id";
55     private static int jobId = 0;
56     private Scheduler scheduler = null;
57     private String JavaDoc servletContextPath;
58     private String JavaDoc schedulerConfigurationPath;
59     private SchedulerStore store = new SchedulerStore();
60
61     /**
62      * Creates a new instance of SchedulerWrapper
63      *
64      * @param servletContextPath The servlet context path.
65      * @param schedulerConfigurationPath The scheduler configuration path.
66      */

67     public SchedulerWrapper(String JavaDoc servletContextPath, String JavaDoc schedulerConfigurationPath) {
68         this.servletContextPath = servletContextPath;
69         this.schedulerConfigurationPath = schedulerConfigurationPath;
70
71         SchedulerFactory factory = new StdSchedulerFactory();
72         log.info("------- Starting up -----------------------");
73
74         try {
75             scheduler = factory.getScheduler();
76
77             scheduler.addSchedulerListener(new AbstractSchedulerListener());
78             scheduler.start();
79         } catch (SchedulerException e) {
80             log.error("Can't initialize SchedulerWrapper: ", e);
81             log.error("------- Startup failed -------------------");
82         }
83
84         log.info("------- Startup complete ------------------");
85     }
86
87     /**
88      * Returns the store.
89      * @return A scheduler store.
90      */

91     protected SchedulerStore getStore() {
92         return store;
93     }
94
95     /**
96      * Returns the scheduler.
97      * @return A scheduler.
98      */

99     private Scheduler getScheduler() {
100         return scheduler;
101     }
102
103     /**
104      * Shuts down the scheduler.
105      */

106     public void shutdown() {
107         log.info("------- Shutting Down ---------------------");
108
109         // try to save state here
110
try {
111             getScheduler().shutdown();
112         } catch (SchedulerException e) {
113             log.error("------- Shutdown Failed -----------------", e);
114         }
115
116         log.info("------- Shutdown Complete -----------------");
117     }
118
119     /**
120      * Returns the servlet context path.
121      * @return The servlet context path.
122      */

123     protected String JavaDoc getServletContextPath() {
124         return servletContextPath;
125     }
126
127     /**
128      * Returns the scheduler configuration path.
129      * @return A string.
130      */

131     protected String JavaDoc getSchedulerConfigurationPath() {
132         return schedulerConfigurationPath;
133     }
134
135     /**
136      * Returns the next job ID to use (calculated using the current time).
137      * @return A string.
138      */

139     protected synchronized static String JavaDoc getNextJobId() {
140         return "job_" + jobId++ +System.currentTimeMillis();
141     }
142
143     /**
144      * Adds a job.
145      * @param jobGroup The job group.
146      * @param startTime The start time.
147      * @param jobClass The class of the job.
148      * @param map The job parameters.
149      * @throws SchedulerException if an error occurs.
150      * @throws PublicationException if an error occurs.
151      */

152     protected void addJob(String JavaDoc jobGroup, Date JavaDoc startTime, Class JavaDoc jobClass, JobDataMap map)
153         throws SchedulerException, PublicationException {
154         String JavaDoc uniqueJobId = getNextJobId();
155         log.debug("Job ID: [" + uniqueJobId + "]");
156         
157         JobDetail jobDetail = new JobDetail(uniqueJobId, jobGroup, jobClass);
158         jobDetail.setJobDataMap(map);
159         
160         Date JavaDoc now = new GregorianCalendar JavaDoc().getTime();
161         if (log.isDebugEnabled()) {
162             DateFormat JavaDoc format = new SimpleDateFormat JavaDoc();
163             log.debug("Trigger time: [" + format.format(startTime) + "]");
164             log.debug("Current time: [" + format.format(now) + "]");
165         }
166         
167         if (startTime.after(now)) {
168             Trigger trigger =
169                 TriggerHelper.createSimpleTrigger(uniqueJobId, jobGroup, startTime);
170             addJob(jobDetail, trigger);
171             log.debug("Scheduling job.");
172         } else {
173             addJob(jobDetail);
174             log.debug("Adding job without scheduling.");
175         }
176         
177         log.debug("----------------------------------------------");
178         
179         store.writeSnapshot(getPublication(jobGroup), getJobWrappers(jobGroup));
180     }
181
182     /**
183      * Adds a job.
184      * @param jobGroup The job group.
185      * @param startTime The start time.
186      * @param request The request to obtain the parameters from.
187      * @throws SchedulerException when something went wrong.
188      */

189     public void addJob(String JavaDoc jobGroup, Date JavaDoc startTime, HttpServletRequest JavaDoc request)
190         throws SchedulerException {
191
192         if (jobGroup == null) {
193             throw new SchedulerException("Job group must not be null!");
194         }
195
196         try {
197             log.debug("----------------------------------------------");
198             log.debug("Adding Job for group [" + jobGroup + "]");
199
200             // FIXME: more flexible
201
Class JavaDoc jobClass = TaskJob.class;
202
203             ServletJob job = ServletJobFactory.createJob(jobClass);
204             JobDataMap map = job.createJobData(request);
205
206             addJob(jobGroup, startTime, jobClass, map);
207         } catch (Exception JavaDoc e) {
208             log.error("Adding job failed: ", e);
209             throw new SchedulerException(e);
210         }
211     }
212
213     /**
214      * Returns the publication for a job group.
215      * @param jobGroup A job group.
216      * @return A publication.
217      * @throws PublicationException when the publication does not exist.
218      */

219     protected Publication getPublication(String JavaDoc jobGroup) throws PublicationException {
220         return PublicationFactory.getPublication(jobGroup, getServletContextPath());
221     }
222
223     /**
224      * Adds a job.
225      * @param detail The job information.
226      * @param trigger The trigger to trigger the job.
227      */

228     protected void addJob(JobDetail detail, Trigger trigger) {
229         try {
230             detail.setDurability(true);
231
232             Date JavaDoc ft = getScheduler().scheduleJob(detail, trigger);
233             log.debug("Job " + detail.getFullName() + " will run at: " + ft);
234         } catch (Exception JavaDoc e) {
235             log.error("Adding job failed: ", e);
236         }
237     }
238
239     /**
240      * Adds a job.
241      * @param detail The job information.
242      */

243     protected void addJob(JobDetail detail) {
244         try {
245             detail.setDurability(true);
246             getScheduler().addJob(detail, true);
247         } catch (SchedulerException e) {
248             log.error("Adding job failed: ", e);
249         }
250     }
251
252     /**
253      * Deletes a job.
254      * @param jobName The job name.
255      * @param jobGroup The job group.
256      */

257     protected void deleteJob(String JavaDoc jobName, String JavaDoc jobGroup) {
258         try {
259             log.debug("-----------------------------------");
260             log.debug("\n Deleting job [" + jobGroup + "/" + jobName + "]");
261             log.debug("-----------------------------------");
262             getScheduler().deleteJob(jobName, jobGroup);
263             getStore().writeSnapshot(getPublication(jobGroup), getJobWrappers(jobGroup));
264         } catch (Exception JavaDoc e) {
265             log.error("Deleting job failed: ", e);
266         }
267     }
268
269     /**
270      * Reads the scheduler configuration.
271      * @return A configuration.
272      */

273     protected Configuration getSchedulerConfiguration() {
274         try {
275             DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
276             String JavaDoc path = getServletContextPath() + getSchedulerConfigurationPath();
277             log.debug("Initializing scheduler configuration: " + path);
278
279             File JavaDoc configurationFile = new File JavaDoc(path);
280             Configuration configuration = builder.buildFromFile(configurationFile);
281
282             return configuration;
283         } catch (Exception JavaDoc e) {
284             log.error("Can't initialize scheduler configuration: ", e);
285
286             return null;
287         }
288     }
289
290     public static final String JavaDoc ELEMENT_TRIGGERS = "triggers";
291     public static final String JavaDoc ELEMENT_TRIGGER = "trigger";
292     public static final String JavaDoc TYPE_ATTRIBUTE = "type";
293     public static final String JavaDoc CLASS_ATTRIBUTE = "class";
294
295     /**
296      * Returns an XML element containing the trigger types.
297      * @param helper The namespace helper of the document that shall contain the element.
298      * @return An XML element.
299      */

300     protected Element JavaDoc getTriggerTypes(NamespaceHelper helper) {
301         try {
302             Configuration configuration = getSchedulerConfiguration();
303             Configuration[] triggerConfigurations =
304                 configuration.getChild(ELEMENT_TRIGGERS).getChildren(ELEMENT_TRIGGER);
305
306             Element JavaDoc triggersElement = helper.createElement("triggers");
307
308             for (int i = 0; i < triggerConfigurations.length; i++) {
309                 Configuration conf = triggerConfigurations[i];
310                 String JavaDoc type = conf.getAttribute(TYPE_ATTRIBUTE);
311                 String JavaDoc className = conf.getAttribute(CLASS_ATTRIBUTE);
312
313                 Element JavaDoc triggerElement = helper.createElement("trigger");
314                 triggerElement.setAttribute("name", type);
315                 triggerElement.setAttribute("src", className);
316                 triggersElement.appendChild(triggerElement);
317             }
318
319             return triggersElement;
320         } catch (Exception JavaDoc e) {
321             log.error("Can't configure trigger types: " + e);
322
323             return null;
324         }
325     }
326
327     /**
328      * Returns the trigger of a certain job.
329      * @param jobName The job name.
330      * @param jobGroup The job group.
331      * @return A trigger.
332      * @throws SchedulerException when something went wrong.
333      */

334     protected Trigger getTrigger(String JavaDoc jobName, String JavaDoc jobGroup) throws SchedulerException {
335         log.debug("Resolving trigger for job [" + jobName + " ][ " + jobGroup + "]");
336         String JavaDoc[] triggerGroups = getScheduler().getTriggerGroupNames();
337
338         for (int groupIndex = 0; groupIndex < triggerGroups.length; groupIndex++) {
339             String JavaDoc[] triggerNames = getScheduler().getTriggerNames(triggerGroups[groupIndex]);
340
341             for (int nameIndex = 0; nameIndex < triggerNames.length; nameIndex++) {
342                 log.debug("Trigger name: " + triggerNames[nameIndex]);
343
344                 Trigger trigger =
345                     getScheduler().getTrigger(triggerNames[nameIndex], triggerGroups[groupIndex]);
346                 log.debug("Job group: " + trigger.getJobGroup());
347
348                 if (trigger.getJobGroup().equals(jobGroup)
349                     && trigger.getJobName().equals(jobName)) {
350                     return trigger;
351                 }
352             }
353         }
354
355         return null;
356     }
357
358     /**
359      * Return an XML description certain job groups.
360      * @param jobGroupNames The job group names.
361      * @return An XML document.
362      * @exception SchedulerException if an error occurs
363      */

364     public Document JavaDoc getSnapshot(String JavaDoc[] jobGroupNames) throws SchedulerException {
365         log.debug("Creating job snapshot");
366
367         NamespaceHelper helper = SchedulerStore.getNamespaceHelper();
368         Document JavaDoc document = helper.getDocument();
369         Element JavaDoc root = document.getDocumentElement();
370
371         // print a list of all available trigger types
372
root.appendChild(getTriggerTypes(helper));
373
374         for (int groupIndex = 0; groupIndex < jobGroupNames.length; groupIndex++) {
375             log.debug("Creating job snapshot for group [" + jobGroupNames[groupIndex] + "]");
376             root.appendChild(getSnapshot(helper, jobGroupNames[groupIndex]));
377         }
378
379         return document;
380     }
381
382     /**
383      * Returns the snapshot of a certain job group.
384      * @param helper The namespace helper.
385      * @param group The job group.
386      * @return An XML element.
387      * @throws SchedulerException when something went wrong.
388      */

389     protected Element JavaDoc getSnapshot(NamespaceHelper helper, String JavaDoc group) throws SchedulerException {
390         JobWrapper[] jobs = getJobWrappers(group);
391         Element JavaDoc element;
392         try {
393             element = getStore().createSnapshot(helper, getPublication(group), jobs);
394         } catch (SchedulerException e) {
395             throw e;
396         } catch (PublicationException e) {
397             throw new SchedulerException(e);
398         }
399         return element;
400     }
401
402     /**
403      * Returns the job wrappers for a certain job group.
404      * @param jobGroupName The job group.
405      * @return An array of job wrappers.
406      * @throws SchedulerException when something went wrong.
407      */

408     protected JobWrapper[] getJobWrappers(String JavaDoc jobGroupName) throws SchedulerException {
409
410         List JavaDoc wrappers = new ArrayList JavaDoc();
411         String JavaDoc[] jobNames = getScheduler().getJobNames(jobGroupName);
412
413         for (int nameIndex = 0; nameIndex < jobNames.length; nameIndex++) {
414             JobDetail jobDetail = getScheduler().getJobDetail(jobNames[nameIndex], jobGroupName);
415             Trigger trigger = getTrigger(jobNames[nameIndex], jobGroupName);
416             wrappers.add(new JobWrapper(jobDetail, trigger));
417         }
418
419         return (JobWrapper[]) wrappers.toArray(new JobWrapper[wrappers.size()]);
420     }
421
422     /**
423      * Return an xml description of all scheduled jobs.
424      * @return DOCUMENT ME!
425      * @exception SchedulerException if an error occurs
426      */

427     public Document JavaDoc getSnapshot() throws SchedulerException {
428         String JavaDoc[] jobGroupNames = getScheduler().getJobGroupNames();
429         return getSnapshot(jobGroupNames);
430     }
431
432     /**
433      * Restores the jobs of a certain job group from the snapshot file.
434      * @param jobGroup The job group.
435      * @throws SchedulerException when something went wrong.
436      */

437     public void restoreJobs(String JavaDoc jobGroup) throws SchedulerException {
438
439         log.debug("--------------------------------------------------");
440         log.debug("Restoring jobs for job group [" + jobGroup + "]");
441         log.debug("--------------------------------------------------");
442
443         try {
444             JobWrapper[] jobs = getStore().restoreJobs(getPublication(jobGroup));
445             for (int i = 0; i < jobs.length; i++) {
446                 if (jobs[i].getTrigger() != null) {
447                     if (log.isDebugEnabled()) {
448                         log.debug(" Trigger time in future - scheduling job.");
449                     }
450                     addJob(jobs[i].getJobDetail(), jobs[i].getTrigger());
451                 } else {
452                     if (log.isDebugEnabled()) {
453                         log.debug(" Trigger time has expired - adding job without scheduling.");
454                     }
455                     addJob(jobs[i].getJobDetail());
456                 }
457             }
458         } catch (Exception JavaDoc e) {
459             log.error("Restoring jobs failed: ", e);
460         }
461
462     }
463
464     /**
465      * Modifies the execution time of a job.
466      * @param jobId The job ID.
467      * @param jobGroup The job group.
468      * @param startTime The new start time.
469      * @throws SchedulerException when the job was not found.
470      */

471     public void modifyJob(String JavaDoc jobId, String JavaDoc jobGroup, Date JavaDoc startTime)
472         throws SchedulerException {
473         log.debug("Modifying job [" + jobId + "][" + jobGroup + "]");
474
475         JobDetail jobDetail = getScheduler().getJobDetail(jobId, jobGroup);
476         if (jobDetail == null) {
477             throw new SchedulerException("Job not found!");
478         }
479
480         Trigger trigger = getTrigger(jobDetail.getName(), jobGroup);
481         if (trigger == null) {
482             log.debug(" No trigger found.");
483         } else {
484             log.debug(" Trigger found. Setting new start time.");
485             jobDetail.setDurability(true);
486             if (startTime.after(new GregorianCalendar JavaDoc().getTime())) {
487                 log.debug(" Start time is in future - re-scheduling job.");
488                 getScheduler().unscheduleJob(trigger.getName(), trigger.getGroup());
489                 trigger = TriggerHelper.createSimpleTrigger(jobId, jobGroup, startTime);
490                 getScheduler().scheduleJob(trigger);
491             } else {
492                 log.debug(" Start time has already expired - deleting job.");
493                 getScheduler().deleteJob(jobId, jobGroup);
494             }
495             try {
496                 getStore().writeSnapshot(getPublication(jobGroup), getJobWrappers(jobGroup));
497             } catch (SchedulerException e) {
498                 throw e;
499             } catch (PublicationException e) {
500                 throw new SchedulerException(e);
501             }
502         }
503     }
504
505     /**
506      * Deletes the jobs for a certain document. This method is called when
507      * a document has been moved or deleted.
508      * @param document A document.
509      * @throws SchedulerException when something went wrong.
510      * @throws PublicationException when something went wrong.
511      */

512     public void deleteJobs(org.apache.lenya.cms.publication.Document document)
513         throws SchedulerException, PublicationException {
514             
515         log.debug("Deleting jobs for document [" + document + "]");
516             
517         String JavaDoc jobGroup = document.getPublication().getId();
518         JobWrapper[] jobs = getJobWrappers(jobGroup);
519         boolean changed = false;
520         for (int i = 0; i < jobs.length; i++) {
521             ServletJob job = jobs[i].getJob();
522             String JavaDoc documentUrl = job.getDocumentUrl(jobs[i].getJobDetail());
523             if (documentUrl.equals(document.getCompleteURL())) {
524                 deleteJob(jobs[i].getJobDetail().getName(), jobGroup);
525                 changed = true;
526             }
527         }
528         if (changed) {
529             getStore().writeSnapshot(getPublication(jobGroup), getJobWrappers(jobGroup));
530         }
531     }
532
533 }
534
Popular Tags