1 /*
2 * Copyright 2004-2005 OpenSymphony
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 *
16 */
17
18 /*
19 * Previously Copyright (c) 2001-2004 James House
20 */
21 package org.quartz.plugins.history;
22
23 import java.text.MessageFormat;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.quartz.JobExecutionContext;
28 import org.quartz.Scheduler;
29 import org.quartz.SchedulerException;
30 import org.quartz.Trigger;
31 import org.quartz.TriggerListener;
32 import org.quartz.spi.SchedulerPlugin;
33
34 /**
35 * Logs a history of all trigger firings via the Jakarta Commons-Logging
36 * framework.
37 *
38 * <p>
39 * The logged message is customizable by setting one of the following message
40 * properties to a String that conforms to the syntax of <code>java.util.MessageFormat</code>.
41 * </p>
42 *
43 * <p>
44 * TriggerFiredMessage - available message data are: <table>
45 * <tr>
46 * <th>Element</th>
47 * <th>Data Type</th>
48 * <th>Description</th>
49 * </tr>
50 * <tr>
51 * <td>0</td>
52 * <td>String</td>
53 * <td>The Trigger's Name.</td>
54 * </tr>
55 * <tr>
56 * <td>1</td>
57 * <td>String</td>
58 * <td>The Trigger's Group.</td>
59 * </tr>
60 * <tr>
61 * <td>2</td>
62 * <td>Date</td>
63 * <td>The scheduled fire time.</td>
64 * </tr>
65 * <tr>
66 * <td>3</td>
67 * <td>Date</td>
68 * <td>The next scheduled fire time.</td>
69 * </tr>
70 * <tr>
71 * <td>4</td>
72 * <td>Date</td>
73 * <td>The actual fire time.</td>
74 * </tr>
75 * <tr>
76 * <td>5</td>
77 * <td>String</td>
78 * <td>The Job's name.</td>
79 * </tr>
80 * <tr>
81 * <td>6</td>
82 * <td>String</td>
83 * <td>The Job's group.</td>
84 * </tr>
85 * <tr>
86 * <td>7</td>
87 * <td>Integer</td>
88 * <td>The re-fire count from the JobExecutionContext.</td>
89 * </tr>
90 * </table>
91 *
92 * The default message text is <i>"Trigger {1}.{0} fired job {6}.{5} at: {4,
93 * date, HH:mm:ss MM/dd/yyyy}"</i>
94 * </p>
95 *
96 * <p>
97 * TriggerMisfiredMessage - available message data are: <table>
98 * <tr>
99 * <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, JobExecutionContext context) {
375 if (!getLog().isInfoEnabled()) {
376 return;
377 }
378
379 Object[] args = {
380 trigger.getName(), trigger.getGroup(),
381 trigger.getPreviousFireTime(), trigger.getNextFireTime(),
382 new java.util.Date(), context.getJobDetail().getName(),
383 context.getJobDetail().getGroup(),
384 new Integer(context.getRefireCount())
385 };
386
387 getLog().info(MessageFormat.format(getTriggerFiredMessage(), args));
388 }
389
390 public void triggerMisfired(Trigger trigger) {
391 if (!getLog().isInfoEnabled()) {
392 return;
393 }
394
395 Object[] args = {
396 trigger.getName(), trigger.getGroup(),
397 trigger.getPreviousFireTime(), trigger.getNextFireTime(),
398 new java.util.Date(), trigger.getJobGroup(),
399 trigger.getJobGroup()
400 };
401
402 getLog().info(MessageFormat.format(getTriggerMisfiredMessage(), args));
403 }
404
405 public void triggerComplete(Trigger trigger, JobExecutionContext context,
406 int triggerInstructionCode) {
407 if (!getLog().isInfoEnabled()) {
408 return;
409 }
410
411 String instrCode = "UNKNOWN";
412 if (triggerInstructionCode == Trigger.INSTRUCTION_DELETE_TRIGGER) {
413 instrCode = "DELETE TRIGGER";
414 } else if (triggerInstructionCode == Trigger.INSTRUCTION_NOOP) {
415 instrCode = "DO NOTHING";
416 } else if (triggerInstructionCode == Trigger.INSTRUCTION_RE_EXECUTE_JOB) {
417 instrCode = "RE-EXECUTE JOB";
418 } else if (triggerInstructionCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE) {
419 instrCode = "SET ALL OF JOB'S TRIGGERS COMPLETE";
420 } else if (triggerInstructionCode == Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE) {
421 instrCode = "SET THIS TRIGGER COMPLETE";
422 }
423
424 Object[] args = {
425 trigger.getName(), trigger.getGroup(),
426 trigger.getPreviousFireTime(), trigger.getNextFireTime(),
427 new java.util.Date(), context.getJobDetail().getName(),
428 context.getJobDetail().getGroup(),
429 new Integer(context.getRefireCount()),
430 new Integer(triggerInstructionCode), instrCode
431 };
432
433 getLog().info(MessageFormat.format(getTriggerCompleteMessage(), args));
434 }
435
436 public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
437 return false;
438 }
439
440 }