001: /*
002: * Copyright 2004-2005 OpenSymphony
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy
006: * of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations
014: * under the License.
015: *
016: */
017:
018: /*
019: * Previously Copyright (c) 2001-2004 James House
020: */
021: package org.quartz.plugins.history;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025: import org.quartz.JobExecutionContext;
026: import org.quartz.JobExecutionException;
027: import org.quartz.Scheduler;
028: import org.quartz.SchedulerException;
029: import org.quartz.Trigger;
030: import org.quartz.JobListener;
031: import org.quartz.spi.SchedulerPlugin;
032:
033: import java.text.MessageFormat;
034:
035: /**
036: * Logs a history of all job executions (and execution vetos) via the
037: * Jakarta Commons-Logging framework.
038: *
039: * <p>
040: * The logged message is customizable by setting one of the following message
041: * properties to a String that conforms to the syntax of <code>java.util.MessageFormat</code>.
042: * </p>
043: *
044: * <p>
045: * JobToBeFiredMessage - available message data are: <table>
046: * <tr>
047: * <th>Element</th>
048: * <th>Data Type</th>
049: * <th>Description</th>
050: * </tr>
051: * <tr>
052: * <td>0</td>
053: * <td>String</td>
054: * <td>The Job's Name.</td>
055: * </tr>
056: * <tr>
057: * <td>1</td>
058: * <td>String</td>
059: * <td>The Job's Group.</td>
060: * </tr>
061: * <tr>
062: * <td>2</td>
063: * <td>Date</td>
064: * <td>The current time.</td>
065: * </tr>
066: * <tr>
067: * <td>3</td>
068: * <td>String</td>
069: * <td>The Trigger's name.</td>
070: * </tr>
071: * <tr>
072: * <td>4</td>
073: * <td>String</td>
074: * <td>The Triggers's group.</td>
075: * </tr>
076: * <tr>
077: * <td>5</td>
078: * <td>Date</td>
079: * <td>The scheduled fire time.</td>
080: * </tr>
081: * <tr>
082: * <td>6</td>
083: * <td>Date</td>
084: * <td>The next scheduled fire time.</td>
085: * </tr>
086: * <tr>
087: * <td>7</td>
088: * <td>Integer</td>
089: * <td>The re-fire count from the JobExecutionContext.</td>
090: * </tr>
091: * </table>
092: *
093: * The default message text is <i>"Job {1}.{0} fired (by trigger {4}.{3}) at:
094: * {2, date, HH:mm:ss MM/dd/yyyy}"</i>
095: * </p>
096: *
097: *
098: * <p>
099: * JobSuccessMessage - available message data are: <table>
100: * <tr>
101: * <th>Element</th>
102: * <th>Data Type</th>
103: * <th>Description</th>
104: * </tr>
105: * <tr>
106: * <td>0</td>
107: * <td>String</td>
108: * <td>The Job's Name.</td>
109: * </tr>
110: * <tr>
111: * <td>1</td>
112: * <td>String</td>
113: * <td>The Job's Group.</td>
114: * </tr>
115: * <tr>
116: * <td>2</td>
117: * <td>Date</td>
118: * <td>The current time.</td>
119: * </tr>
120: * <tr>
121: * <td>3</td>
122: * <td>String</td>
123: * <td>The Trigger's name.</td>
124: * </tr>
125: * <tr>
126: * <td>4</td>
127: * <td>String</td>
128: * <td>The Triggers's group.</td>
129: * </tr>
130: * <tr>
131: * <td>5</td>
132: * <td>Date</td>
133: * <td>The scheduled fire time.</td>
134: * </tr>
135: * <tr>
136: * <td>6</td>
137: * <td>Date</td>
138: * <td>The next scheduled fire time.</td>
139: * </tr>
140: * <tr>
141: * <td>7</td>
142: * <td>Integer</td>
143: * <td>The re-fire count from the JobExecutionContext.</td>
144: * </tr>
145: * <tr>
146: * <td>8</td>
147: * <td>Object</td>
148: * <td>The string value (toString() having been called) of the result (if any)
149: * that the Job set on the JobExecutionContext, with on it. "NULL" if no
150: * result was set.</td>
151: * </td>
152: * </tr>
153: * </table>
154: *
155: * The default message text is <i>"Job {1}.{0} execution complete at {2, date,
156: * HH:mm:ss MM/dd/yyyy} and reports: {8}"</i>
157: * </p>
158: *
159: * <p>
160: * JobFailedMessage - available message data are: <table>
161: * <tr>
162: * <th>Element</th>
163: * <th>Data Type</th>
164: * <th>Description</th>
165: * </tr>
166: * <tr>
167: * <td>0</td>
168: * <td>String</td>
169: * <td>The Job's Name.</td>
170: * </tr>
171: * <tr>
172: * <td>1</td>
173: * <td>String</td>
174: * <td>The Job's Group.</td>
175: * </tr>
176: * <tr>
177: * <td>2</td>
178: * <td>Date</td>
179: * <td>The current time.</td>
180: * </tr>
181: * <tr>
182: * <td>3</td>
183: * <td>String</td>
184: * <td>The Trigger's name.</td>
185: * </tr>
186: * <tr>
187: * <td>4</td>
188: * <td>String</td>
189: * <td>The Triggers's group.</td>
190: * </tr>
191: * <tr>
192: * <td>5</td>
193: * <td>Date</td>
194: * <td>The scheduled fire time.</td>
195: * </tr>
196: * <tr>
197: * <td>6</td>
198: * <td>Date</td>
199: * <td>The next scheduled fire time.</td>
200: * </tr>
201: * <tr>
202: * <td>7</td>
203: * <td>Integer</td>
204: * <td>The re-fire count from the JobExecutionContext.</td>
205: * </tr>
206: * <tr>
207: * <td>8</td>
208: * <td>String</td>
209: * <td>The message from the thrown JobExecution Exception.
210: * </td>
211: * </tr>
212: * </table>
213: *
214: * The default message text is <i>"Job {1}.{0} execution failed at {2, date,
215: * HH:mm:ss MM/dd/yyyy} and reports: {8}"</i>
216: * </p>
217: *
218: *
219: * <p>
220: * JobWasVetoedMessage - available message data are: <table>
221: * <tr>
222: * <th>Element</th>
223: * <th>Data Type</th>
224: * <th>Description</th>
225: * </tr>
226: * <tr>
227: * <td>0</td>
228: * <td>String</td>
229: * <td>The Job's Name.</td>
230: * </tr>
231: * <tr>
232: * <td>1</td>
233: * <td>String</td>
234: * <td>The Job's Group.</td>
235: * </tr>
236: * <tr>
237: * <td>2</td>
238: * <td>Date</td>
239: * <td>The current time.</td>
240: * </tr>
241: * <tr>
242: * <td>3</td>
243: * <td>String</td>
244: * <td>The Trigger's name.</td>
245: * </tr>
246: * <tr>
247: * <td>4</td>
248: * <td>String</td>
249: * <td>The Triggers's group.</td>
250: * </tr>
251: * <tr>
252: * <td>5</td>
253: * <td>Date</td>
254: * <td>The scheduled fire time.</td>
255: * </tr>
256: * <tr>
257: * <td>6</td>
258: * <td>Date</td>
259: * <td>The next scheduled fire time.</td>
260: * </tr>
261: * <tr>
262: * <td>7</td>
263: * <td>Integer</td>
264: * <td>The re-fire count from the JobExecutionContext.</td>
265: * </tr>
266: * </table>
267: *
268: * The default message text is <i>"Job {1}.{0} was vetoed. It was to be fired
269: * (by trigger {4}.{3}) at: {2, date, HH:mm:ss MM/dd/yyyy}"</i>
270: * </p>
271: *
272: *
273: * @author James House
274: */
275: public class LoggingJobHistoryPlugin implements SchedulerPlugin,
276: JobListener {
277:
278: /*
279: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
280: *
281: * Data members.
282: *
283: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
284: */
285:
286: private String name;
287:
288: private String jobToBeFiredMessage = "Job {1}.{0} fired (by trigger {4}.{3}) at: {2, date, HH:mm:ss MM/dd/yyyy}";
289:
290: private String jobSuccessMessage = "Job {1}.{0} execution complete at {2, date, HH:mm:ss MM/dd/yyyy} and reports: {8}";
291:
292: private String jobFailedMessage = "Job {1}.{0} execution failed at {2, date, HH:mm:ss MM/dd/yyyy} and reports: {8}";
293:
294: private String jobWasVetoedMessage = "Job {1}.{0} was vetoed. It was to be fired (by trigger {4}.{3}) at: {2, date, HH:mm:ss MM/dd/yyyy}";
295:
296: private final Log log = LogFactory.getLog(getClass());
297:
298: /*
299: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
300: *
301: * Constructors.
302: *
303: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
304: */
305:
306: public LoggingJobHistoryPlugin() {
307: }
308:
309: /*
310: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
311: *
312: * Interface.
313: *
314: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
315: */
316:
317: protected Log getLog() {
318: return log;
319: }
320:
321: /**
322: * Get the message that is logged when a Job successfully completes its
323: * execution.
324: */
325: public String getJobSuccessMessage() {
326: return jobSuccessMessage;
327: }
328:
329: /**
330: * Get the message that is logged when a Job fails its
331: * execution.
332: */
333: public String getJobFailedMessage() {
334: return jobFailedMessage;
335: }
336:
337: /**
338: * Get the message that is logged when a Job is about to execute.
339: */
340: public String getJobToBeFiredMessage() {
341: return jobToBeFiredMessage;
342: }
343:
344: /**
345: * Set the message that is logged when a Job successfully completes its
346: * execution.
347: *
348: * @param jobSuccessMessage
349: * String in java.text.MessageFormat syntax.
350: */
351: public void setJobSuccessMessage(String jobSuccessMessage) {
352: this .jobSuccessMessage = jobSuccessMessage;
353: }
354:
355: /**
356: * Set the message that is logged when a Job fails its
357: * execution.
358: *
359: * @param jobFailedMessage
360: * String in java.text.MessageFormat syntax.
361: */
362: public void setJobFailedMessage(String jobFailedMessage) {
363: this .jobFailedMessage = jobFailedMessage;
364: }
365:
366: /**
367: * Set the message that is logged when a Job is about to execute.
368: *
369: * @param jobToBeFiredMessage
370: * String in java.text.MessageFormat syntax.
371: */
372: public void setJobToBeFiredMessage(String jobToBeFiredMessage) {
373: this .jobToBeFiredMessage = jobToBeFiredMessage;
374: }
375:
376: /**
377: * Get the message that is logged when a Job execution is vetoed by a
378: * trigger listener.
379: */
380: public String getJobWasVetoedMessage() {
381: return jobWasVetoedMessage;
382: }
383:
384: /**
385: * Set the message that is logged when a Job execution is vetoed by a
386: * trigger listener.
387: *
388: * @param jobWasVetoedMessage
389: * String in java.text.MessageFormat syntax.
390: */
391: public void setJobWasVetoedMessage(String jobWasVetoedMessage) {
392: this .jobWasVetoedMessage = jobWasVetoedMessage;
393: }
394:
395: /*
396: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
397: *
398: * SchedulerPlugin Interface.
399: *
400: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
401: */
402:
403: /**
404: * <p>
405: * Called during creation of the <code>Scheduler</code> in order to give
406: * the <code>SchedulerPlugin</code> a chance to initialize.
407: * </p>
408: *
409: * @throws SchedulerConfigException
410: * if there is an error initializing.
411: */
412: public void initialize(String name, Scheduler scheduler)
413: throws SchedulerException {
414: this .name = name;
415: scheduler.addGlobalJobListener(this );
416: }
417:
418: public void start() {
419: // do nothing...
420: }
421:
422: /**
423: * <p>
424: * Called in order to inform the <code>SchedulerPlugin</code> that it
425: * should free up all of it's resources because the scheduler is shutting
426: * down.
427: * </p>
428: */
429: public void shutdown() {
430: // nothing to do...
431: }
432:
433: /*
434: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
435: *
436: * JobListener Interface.
437: *
438: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
439: */
440:
441: /*
442: * Object[] arguments = { new Integer(7), new
443: * Date(System.currentTimeMillis()), "a disturbance in the Force" };
444: *
445: * String result = MessageFormat.format( "At {1,time} on {1,date}, there
446: * was {2} on planet {0,number,integer}.", arguments);
447: */
448:
449: public String getName() {
450: return name;
451: }
452:
453: /**
454: * @see org.quartz.JobListener#jobToBeExecuted(JobExecutionContext)
455: */
456: public void jobToBeExecuted(JobExecutionContext context) {
457: if (!getLog().isInfoEnabled()) {
458: return;
459: }
460:
461: Trigger trigger = context.getTrigger();
462:
463: Object[] args = { context.getJobDetail().getName(),
464: context.getJobDetail().getGroup(),
465: new java.util.Date(), trigger.getName(),
466: trigger.getGroup(), trigger.getPreviousFireTime(),
467: trigger.getNextFireTime(),
468: new Integer(context.getRefireCount()) };
469:
470: getLog().info(
471: MessageFormat.format(getJobToBeFiredMessage(), args));
472: }
473:
474: /**
475: * @see org.quartz.JobListener#jobWasExecuted(JobExecutionContext, JobExecutionException)
476: */
477: public void jobWasExecuted(JobExecutionContext context,
478: JobExecutionException jobException) {
479:
480: Trigger trigger = context.getTrigger();
481:
482: Object[] args = null;
483:
484: if (jobException != null) {
485: if (!getLog().isWarnEnabled()) {
486: return;
487: }
488:
489: String errMsg = jobException.getMessage();
490: args = new Object[] { context.getJobDetail().getName(),
491: context.getJobDetail().getGroup(),
492: new java.util.Date(), trigger.getName(),
493: trigger.getGroup(), trigger.getPreviousFireTime(),
494: trigger.getNextFireTime(),
495: new Integer(context.getRefireCount()), errMsg };
496:
497: getLog().warn(
498: MessageFormat.format(getJobFailedMessage(), args),
499: jobException);
500: } else {
501: if (!getLog().isInfoEnabled()) {
502: return;
503: }
504:
505: String result = String.valueOf(context.getResult());
506: args = new Object[] { context.getJobDetail().getName(),
507: context.getJobDetail().getGroup(),
508: new java.util.Date(), trigger.getName(),
509: trigger.getGroup(), trigger.getPreviousFireTime(),
510: trigger.getNextFireTime(),
511: new Integer(context.getRefireCount()), result };
512:
513: getLog().info(
514: MessageFormat.format(getJobSuccessMessage(), args));
515: }
516: }
517:
518: /**
519: * @see org.quartz.JobListener#jobExecutionVetoed(org.quartz.JobExecutionContext)
520: */
521: public void jobExecutionVetoed(JobExecutionContext context) {
522:
523: if (!getLog().isInfoEnabled()) {
524: return;
525: }
526:
527: Trigger trigger = context.getTrigger();
528:
529: Object[] args = { context.getJobDetail().getName(),
530: context.getJobDetail().getGroup(),
531: new java.util.Date(), trigger.getName(),
532: trigger.getGroup(), trigger.getPreviousFireTime(),
533: trigger.getNextFireTime(),
534: new Integer(context.getRefireCount()) };
535:
536: getLog().info(
537: MessageFormat.format(getJobWasVetoedMessage(), args));
538: }
539:
540: }
541:
542: // EOF
|