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 java.text.MessageFormat;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.quartz.JobExecutionContext;
028: import org.quartz.Scheduler;
029: import org.quartz.SchedulerException;
030: import org.quartz.Trigger;
031: import org.quartz.TriggerListener;
032: import org.quartz.spi.SchedulerPlugin;
033:
034: /**
035: * Logs a history of all trigger firings via the Jakarta Commons-Logging
036: * framework.
037: *
038: * <p>
039: * The logged message is customizable by setting one of the following message
040: * properties to a String that conforms to the syntax of <code>java.util.MessageFormat</code>.
041: * </p>
042: *
043: * <p>
044: * TriggerFiredMessage - available message data are: <table>
045: * <tr>
046: * <th>Element</th>
047: * <th>Data Type</th>
048: * <th>Description</th>
049: * </tr>
050: * <tr>
051: * <td>0</td>
052: * <td>String</td>
053: * <td>The Trigger's Name.</td>
054: * </tr>
055: * <tr>
056: * <td>1</td>
057: * <td>String</td>
058: * <td>The Trigger's Group.</td>
059: * </tr>
060: * <tr>
061: * <td>2</td>
062: * <td>Date</td>
063: * <td>The scheduled fire time.</td>
064: * </tr>
065: * <tr>
066: * <td>3</td>
067: * <td>Date</td>
068: * <td>The next scheduled fire time.</td>
069: * </tr>
070: * <tr>
071: * <td>4</td>
072: * <td>Date</td>
073: * <td>The actual fire time.</td>
074: * </tr>
075: * <tr>
076: * <td>5</td>
077: * <td>String</td>
078: * <td>The Job's name.</td>
079: * </tr>
080: * <tr>
081: * <td>6</td>
082: * <td>String</td>
083: * <td>The Job's group.</td>
084: * </tr>
085: * <tr>
086: * <td>7</td>
087: * <td>Integer</td>
088: * <td>The re-fire count from the JobExecutionContext.</td>
089: * </tr>
090: * </table>
091: *
092: * The default message text is <i>"Trigger {1}.{0} fired job {6}.{5} at: {4,
093: * date, HH:mm:ss MM/dd/yyyy}"</i>
094: * </p>
095: *
096: * <p>
097: * TriggerMisfiredMessage - available message data are: <table>
098: * <tr>
099: * <th>Element</th>
100: * <th>Data Type</th>
101: * <th>Description</th>
102: * </tr>
103: * <tr>
104: * <td>0</td>
105: * <td>String</td>
106: * <td>The Trigger's Name.</td>
107: * </tr>
108: * <tr>
109: * <td>1</td>
110: * <td>String</td>
111: * <td>The Trigger's Group.</td>
112: * </tr>
113: * <tr>
114: * <td>2</td>
115: * <td>Date</td>
116: * <td>The scheduled fire time.</td>
117: * </tr>
118: * <tr>
119: * <td>3</td>
120: * <td>Date</td>
121: * <td>The next scheduled fire time.</td>
122: * </tr>
123: * <tr>
124: * <td>4</td>
125: * <td>Date</td>
126: * <td>The actual fire time. (the time the misfire was detected/handled)</td>
127: * </tr>
128: * <tr>
129: * <td>5</td>
130: * <td>String</td>
131: * <td>The Job's name.</td>
132: * </tr>
133: * <tr>
134: * <td>6</td>
135: * <td>String</td>
136: * <td>The Job's group.</td>
137: * </tr>
138: * </table>
139: *
140: * The default message text is <i>"Trigger {1}.{0} misfired job {6}.{5} at:
141: * {4, date, HH:mm:ss MM/dd/yyyy}. Should have fired at: {3, date, HH:mm:ss
142: * MM/dd/yyyy}"</i>
143: * </p>
144: *
145: * <p>
146: * TriggerCompleteMessage - available message data are: <table>
147: * <tr>
148: * <th>Element</th>
149: * <th>Data Type</th>
150: * <th>Description</th>
151: * </tr>
152: * <tr>
153: * <td>0</td>
154: * <td>String</td>
155: * <td>The Trigger's Name.</td>
156: * </tr>
157: * <tr>
158: * <td>1</td>
159: * <td>String</td>
160: * <td>The Trigger's Group.</td>
161: * </tr>
162: * <tr>
163: * <td>2</td>
164: * <td>Date</td>
165: * <td>The scheduled fire time.</td>
166: * </tr>
167: * <tr>
168: * <td>3</td>
169: * <td>Date</td>
170: * <td>The next scheduled fire time.</td>
171: * </tr>
172: * <tr>
173: * <td>4</td>
174: * <td>Date</td>
175: * <td>The job completion time.</td>
176: * </tr>
177: * <tr>
178: * <td>5</td>
179: * <td>String</td>
180: * <td>The Job's name.</td>
181: * </tr>
182: * <tr>
183: * <td>6</td>
184: * <td>String</td>
185: * <td>The Job's group.</td>
186: * </tr>
187: * <tr>
188: * <td>7</td>
189: * <td>Integer</td>
190: * <td>The re-fire count from the JobExecutionContext.</td>
191: * </tr>
192: * <tr>
193: * <td>8</td>
194: * <td>Integer</td>
195: * <td>The trigger's resulting instruction code.</td>
196: * </tr>
197: * <tr>
198: * <td>9</td>
199: * <td>String</td>
200: * <td>A human-readable translation of the trigger's resulting instruction
201: * code.</td>
202: * </tr>
203: * </table>
204: *
205: * The default message text is <i>"Trigger {1}.{0} completed firing job
206: * {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction
207: * code: {9}"</i>
208: * </p>
209: *
210: * @author James House
211: */
212: public class LoggingTriggerHistoryPlugin implements SchedulerPlugin,
213: TriggerListener {
214:
215: /*
216: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
217: *
218: * Data members.
219: *
220: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
221: */
222:
223: private String name;
224:
225: private String triggerFiredMessage = "Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}";
226:
227: private String triggerMisfiredMessage = "Trigger {1}.{0} misfired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}. Should have fired at: {3, date, HH:mm:ss MM/dd/yyyy}";
228:
229: private String triggerCompleteMessage = "Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {9}";
230:
231: private final Log log = LogFactory.getLog(getClass());
232:
233: /*
234: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
235: *
236: * Constructors.
237: *
238: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
239: */
240:
241: public LoggingTriggerHistoryPlugin() {
242: }
243:
244: /*
245: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
246: *
247: * Interface.
248: *
249: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
250: */
251:
252: protected Log getLog() {
253: return log;
254: }
255:
256: /**
257: * Get the message that is printed upon the completion of a trigger's
258: * firing.
259: *
260: * @return String
261: */
262: public String getTriggerCompleteMessage() {
263: return triggerCompleteMessage;
264: }
265:
266: /**
267: * Get the message that is printed upon a trigger's firing.
268: *
269: * @return String
270: */
271: public String getTriggerFiredMessage() {
272: return triggerFiredMessage;
273: }
274:
275: /**
276: * Get the message that is printed upon a trigger's mis-firing.
277: *
278: * @return String
279: */
280: public String getTriggerMisfiredMessage() {
281: return triggerMisfiredMessage;
282: }
283:
284: /**
285: * Set the message that is printed upon the completion of a trigger's
286: * firing.
287: *
288: * @param triggerCompleteMessage
289: * String in java.text.MessageFormat syntax.
290: */
291: public void setTriggerCompleteMessage(String triggerCompleteMessage) {
292: this .triggerCompleteMessage = triggerCompleteMessage;
293: }
294:
295: /**
296: * Set the message that is printed upon a trigger's firing.
297: *
298: * @param triggerFiredMessage
299: * String in java.text.MessageFormat syntax.
300: */
301: public void setTriggerFiredMessage(String triggerFiredMessage) {
302: this .triggerFiredMessage = triggerFiredMessage;
303: }
304:
305: /**
306: * Set the message that is printed upon a trigger's firing.
307: *
308: * @param triggerMisfiredMessage
309: * String in java.text.MessageFormat syntax.
310: */
311: public void setTriggerMisfiredMessage(String triggerMisfiredMessage) {
312: this .triggerMisfiredMessage = triggerMisfiredMessage;
313: }
314:
315: /*
316: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
317: *
318: * SchedulerPlugin Interface.
319: *
320: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
321: */
322:
323: /**
324: * <p>
325: * Called during creation of the <code>Scheduler</code> in order to give
326: * the <code>SchedulerPlugin</code> a chance to initialize.
327: * </p>
328: *
329: * @throws SchedulerConfigException
330: * if there is an error initializing.
331: */
332: public void initialize(String name, Scheduler scheduler)
333: throws SchedulerException {
334: this .name = name;
335:
336: scheduler.addGlobalTriggerListener(this );
337: }
338:
339: public void start() {
340: // do nothing...
341: }
342:
343: /**
344: * <p>
345: * Called in order to inform the <code>SchedulerPlugin</code> that it
346: * should free up all of it's resources because the scheduler is shutting
347: * down.
348: * </p>
349: */
350: public void shutdown() {
351: // nothing to do...
352: }
353:
354: /*
355: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
356: *
357: * TriggerListener Interface.
358: *
359: * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
360: */
361:
362: /*
363: * Object[] arguments = { new Integer(7), new
364: * Date(System.currentTimeMillis()), "a disturbance in the Force" };
365: *
366: * String result = MessageFormat.format( "At {1,time} on {1,date}, there
367: * was {2} on planet {0,number,integer}.", arguments);
368: */
369:
370: public String getName() {
371: return name;
372: }
373:
374: public void triggerFired(Trigger trigger,
375: JobExecutionContext context) {
376: if (!getLog().isInfoEnabled()) {
377: return;
378: }
379:
380: Object[] args = { trigger.getName(), trigger.getGroup(),
381: trigger.getPreviousFireTime(),
382: trigger.getNextFireTime(), new java.util.Date(),
383: context.getJobDetail().getName(),
384: context.getJobDetail().getGroup(),
385: new Integer(context.getRefireCount()) };
386:
387: getLog().info(
388: MessageFormat.format(getTriggerFiredMessage(), args));
389: }
390:
391: public void triggerMisfired(Trigger trigger) {
392: if (!getLog().isInfoEnabled()) {
393: return;
394: }
395:
396: Object[] args = { trigger.getName(), trigger.getGroup(),
397: trigger.getPreviousFireTime(),
398: trigger.getNextFireTime(), new java.util.Date(),
399: trigger.getJobGroup(), trigger.getJobGroup() };
400:
401: getLog()
402: .info(
403: MessageFormat.format(
404: getTriggerMisfiredMessage(), args));
405: }
406:
407: public void triggerComplete(Trigger trigger,
408: JobExecutionContext context, int triggerInstructionCode) {
409: if (!getLog().isInfoEnabled()) {
410: return;
411: }
412:
413: String instrCode = "UNKNOWN";
414: if (triggerInstructionCode == Trigger.INSTRUCTION_DELETE_TRIGGER) {
415: instrCode = "DELETE TRIGGER";
416: } else if (triggerInstructionCode == Trigger.INSTRUCTION_NOOP) {
417: instrCode = "DO NOTHING";
418: } else if (triggerInstructionCode == Trigger.INSTRUCTION_RE_EXECUTE_JOB) {
419: instrCode = "RE-EXECUTE JOB";
420: } else if (triggerInstructionCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE) {
421: instrCode = "SET ALL OF JOB'S TRIGGERS COMPLETE";
422: } else if (triggerInstructionCode == Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE) {
423: instrCode = "SET THIS TRIGGER COMPLETE";
424: }
425:
426: Object[] args = { trigger.getName(), trigger.getGroup(),
427: trigger.getPreviousFireTime(),
428: trigger.getNextFireTime(), new java.util.Date(),
429: context.getJobDetail().getName(),
430: context.getJobDetail().getGroup(),
431: new Integer(context.getRefireCount()),
432: new Integer(triggerInstructionCode), instrCode };
433:
434: getLog()
435: .info(
436: MessageFormat.format(
437: getTriggerCompleteMessage(), args));
438: }
439:
440: public boolean vetoJobExecution(Trigger trigger,
441: JobExecutionContext context) {
442: return false;
443: }
444:
445: }
|