001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: /* $Id: SchedulerStore.java 473861 2006-11-12 03:51:14Z gregor $ */
020:
021: package org.apache.lenya.cms.scheduler;
022:
023: import java.io.File;
024: import java.io.IOException;
025: import java.text.DateFormat;
026: import java.text.SimpleDateFormat;
027: import java.util.ArrayList;
028: import java.util.Date;
029: import java.util.GregorianCalendar;
030: import java.util.List;
031:
032: import javax.xml.transform.TransformerConfigurationException;
033: import javax.xml.transform.TransformerException;
034:
035: import org.apache.lenya.cms.publication.Publication;
036: import org.apache.lenya.cms.scheduler.xml.TriggerHelper;
037: import org.apache.lenya.xml.DocumentHelper;
038: import org.apache.lenya.xml.NamespaceHelper;
039: import org.apache.log4j.Logger;
040: import org.quartz.JobDetail;
041: import org.quartz.SchedulerException;
042: import org.quartz.Trigger;
043: import org.w3c.dom.Document;
044: import org.w3c.dom.Element;
045:
046: /**
047: * Store for scheduler jobs.
048: */
049: public class SchedulerStore {
050:
051: /**
052: * <code>ELEMENT_JOB_GROUP</code> The job group element
053: */
054: public static final String ELEMENT_JOB_GROUP = "job-group";
055: /**
056: * <code>ELEMENT_JOB</code> The job element
057: */
058: public static final String ELEMENT_JOB = "job";
059: /**
060: * <code>TITLE_ELEMENT</code> The title element
061: */
062: public static final String TITLE_ELEMENT = "title";
063: /**
064: * <code>SNAPSHOT_FILE</code> The path to the stored jobs
065: */
066: public static final String SNAPSHOT_FILE = "config/scheduler/jobs.xml"
067: .replace('/', File.separatorChar);
068:
069: /** The namespace for the <code>jobs.xml</code> file. */
070: public static final String NAMESPACE = "http://apache.org/cocoon/lenya/scheduler/1.0";
071:
072: private static final Logger log = Logger
073: .getLogger(SchedulerStore.class);
074:
075: /**
076: * Ctor.
077: */
078: public SchedulerStore() {
079: // do nothing
080: }
081:
082: /**
083: * Returns the job snapshot file for a publication..
084: * @param publication The publication.
085: * @return A file.
086: * @throws SchedulerException when the publication could not be built.
087: */
088: protected File getJobsFile(Publication publication)
089: throws SchedulerException {
090: File jobsFile;
091: jobsFile = new File(publication.getDirectory(), SNAPSHOT_FILE);
092: log.debug("Resolved job snapshot file: ["
093: + jobsFile.getAbsolutePath() + "]");
094: return jobsFile;
095: }
096:
097: /**
098: * Writes a job snapshot.
099: * @param publication The publication.
100: * @param jobs The jobs to persist
101: * @throws SchedulerException when something went wrong.
102: */
103: protected void writeSnapshot(Publication publication,
104: JobWrapper[] jobs) throws SchedulerException {
105:
106: log.debug("Writing job snapshot for publication ["
107: + publication.getId() + "]");
108: File jobsFile = getJobsFile(publication);
109:
110: try {
111: File directory = jobsFile.getParentFile();
112:
113: if (!directory.exists()) {
114: directory.mkdirs();
115: log.debug("Creating job snapshot directory: "
116: + directory.getPath());
117: }
118:
119: if (jobsFile.createNewFile())
120: log.debug("new jobs file created.");
121: DocumentHelper.writeDocument(
122: getSnapshot(publication, jobs), jobsFile);
123: } catch (final TransformerConfigurationException e) {
124: log.error("Writing job snapshot failed: ", e);
125: throw new SchedulerException(e);
126: } catch (final IOException e) {
127: log.error("Writing job snapshot failed: ", e);
128: throw new SchedulerException(e);
129: } catch (final TransformerException e) {
130: log.error("Writing job snapshot failed: ", e);
131: throw new SchedulerException(e);
132: } catch (final SchedulerException e) {
133: log.error("Writing job snapshot failed: ", e);
134: throw new SchedulerException(e);
135: }
136:
137: }
138:
139: /**
140: * Return an xml description of all scheduled jobs for the given publication.
141: *
142: * @param publication The publication.
143: * @param jobs The jobs to return
144: * @return An XML document.
145: * @exception SchedulerException if an error occurs
146: */
147: public Document getSnapshot(Publication publication,
148: JobWrapper[] jobs) throws SchedulerException {
149: NamespaceHelper helper = SchedulerStore.getNamespaceHelper();
150: Document document = helper.getDocument();
151: Element root = document.getDocumentElement();
152:
153: log.debug("Creating job snapshot for publication ["
154: + publication.getId() + "]");
155: root.appendChild(createSnapshot(helper, publication, jobs));
156:
157: return document;
158: }
159:
160: /**
161: * Returns a scheduler namespace helper for a document.
162: * @param document The XML document.
163: * @return a namespace helper.
164: */
165: public static NamespaceHelper getNamespaceHelper(Document document) {
166: return new NamespaceHelper(NAMESPACE, "sch", document);
167: }
168:
169: /**
170: * Returns a new scheduler namespace helper with an document containing
171: * a <sch:scheduler> element.
172: * @return a namespace helper.
173: */
174: public static NamespaceHelper getNamespaceHelper() {
175: try {
176: return new NamespaceHelper(NAMESPACE, "sch", "scheduler");
177: } catch (Exception e) {
178: log.error("Could not create namespace helper: ", e);
179:
180: return null;
181: }
182: }
183:
184: /**
185: * Creates an XML element containing a snapshot of a job group.
186: * @param helper The namespace helper to use.
187: * @param publication The publication to create the snapshot for
188: * @param jobs The jobs
189: * @return An XML element.
190: * @throws SchedulerException when something went wrong.
191: */
192: protected Element createSnapshot(NamespaceHelper helper,
193: Publication publication, JobWrapper[] jobs)
194: throws SchedulerException {
195: Element jobGroupElement = helper
196: .createElement(ELEMENT_JOB_GROUP);
197: jobGroupElement.setAttribute("name", publication.getId());
198:
199: for (int i = 0; i < jobs.length; i++) {
200:
201: ServletJob job = jobs[i].getJob();
202: Element jobElement = job.save(helper, jobs[i]
203: .getJobDetail());
204: jobGroupElement.appendChild(jobElement);
205:
206: Trigger trigger = jobs[i].getTrigger();
207:
208: if (trigger != null) {
209: Element triggerElement = TriggerHelper.createElement(
210: helper, trigger);
211: jobElement.appendChild(triggerElement);
212: }
213: }
214:
215: return jobGroupElement;
216: }
217:
218: /**
219: * Restores the jobs of a certain job group from the snapshot file.
220: * @param publication The publication
221: * @return A job wrapper
222: * @throws SchedulerException when something went wrong.
223: */
224: public JobWrapper[] restoreJobs(Publication publication)
225: throws SchedulerException {
226:
227: log.debug("Restoring jobs for publication ["
228: + publication.getId() + "]");
229:
230: List wrappers = new ArrayList();
231: File jobsFile = getJobsFile(publication);
232:
233: if (jobsFile.exists()) {
234: Element[] jobElements = getJobElements(publication);
235: Document document;
236: try {
237: document = DocumentHelper.readDocument(jobsFile);
238: } catch (Exception e) {
239: throw new SchedulerException(e);
240: }
241: NamespaceHelper helper = SchedulerStore
242: .getNamespaceHelper(document);
243:
244: for (int i = 0; i < jobElements.length; i++) {
245: wrappers.add(restoreJob(helper, jobElements[i],
246: publication));
247: }
248: } else {
249: log.debug("Could not restore jobs for publication ["
250: + publication.getId()
251: + "] - jobs file does not exist.");
252: }
253:
254: return (JobWrapper[]) wrappers.toArray(new JobWrapper[wrappers
255: .size()]);
256: }
257:
258: /**
259: * Restores the jobs from a certain XML element.
260: * @param helper The namespace helper
261: * @param jobElement The XML element.
262: * @param publication The publication to restore jobs for
263: * @return A job wrapper
264: * @throws SchedulerException if an error occurs
265: */
266: protected JobWrapper restoreJob(NamespaceHelper helper,
267: Element jobElement, Publication publication)
268: throws SchedulerException {
269: log.debug("Restoring job ");
270: JobWrapper wrapper;
271:
272: try {
273: String jobClassName = jobElement
274: .getAttribute(ServletJob.ATTRIBUTE_CLASS);
275: ServletJob job = ServletJobFactory.createJob(jobClassName);
276: JobDetail jobDetail = job.load(jobElement, publication
277: .getId(), publication.getServletContext()
278: .getAbsolutePath());
279:
280: Trigger trigger = null;
281:
282: Element triggerElement = helper.getFirstChild(jobElement,
283: "trigger");
284: if (triggerElement != null) {
285: trigger = TriggerHelper.createTrigger(triggerElement,
286: jobDetail.getName(), jobDetail.getGroup());
287:
288: Date now = new GregorianCalendar().getTime();
289: if (log.isDebugEnabled()) {
290: DateFormat format = new SimpleDateFormat();
291: log.debug(" Trigger time: ["
292: + format.format(trigger.getFinalFireTime())
293: + "]");
294: log.debug(" Current time: ["
295: + format.format(now) + "]");
296: }
297: if (!trigger.getFinalFireTime().after(now)) {
298: trigger = null;
299: }
300: }
301: wrapper = new JobWrapper(jobDetail, trigger);
302:
303: } catch (Exception e) {
304: throw new SchedulerException(e);
305: }
306: return wrapper;
307: }
308:
309: /**
310: * Returns the job elements of a publication.
311: * @param publication
312: * @return An array of elements
313: * @throws SchedulerException when something went wrong.
314: */
315: protected Element[] getJobElements(Publication publication)
316: throws SchedulerException {
317: Element[] jobElements;
318: try {
319: File jobsFile = getJobsFile(publication);
320: if (jobsFile.exists()) {
321: Document document = DocumentHelper
322: .readDocument(jobsFile);
323: Element schedulerElement = document
324: .getDocumentElement();
325: NamespaceHelper helper = SchedulerStore
326: .getNamespaceHelper(document);
327:
328: Element jobGroupElement = helper.getFirstChild(
329: schedulerElement,
330: SchedulerStore.ELEMENT_JOB_GROUP);
331: if (jobGroupElement == null) {
332: throw new SchedulerException(
333: "No <job-group> element found!");
334: }
335:
336: String jobGroupAttribute = jobGroupElement
337: .getAttribute("name");
338:
339: if (!jobGroupAttribute.equals(publication.getId())) {
340: throw new SchedulerException(
341: "The jobs.xml file contains a wrong job group: ["
342: + jobGroupAttribute + "]");
343: }
344: jobElements = helper.getChildren(jobGroupElement,
345: SchedulerStore.ELEMENT_JOB);
346:
347: } else {
348: throw new SchedulerException("The jobs file ["
349: + jobsFile.getAbsolutePath()
350: + "] does not exist!");
351: }
352: } catch (SchedulerException e) {
353: throw e;
354: } catch (Exception e) {
355: throw new SchedulerException(e);
356: }
357: return jobElements;
358: }
359: }
|