/* * Copyright 2001-2009 Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package org.quartz.plugins.history; import java.text.MessageFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.quartz.JobExecutionContext; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.TriggerListener; import org.quartz.Trigger.CompletedExecutionInstruction; import org.quartz.impl.matchers.EverythingMatcher; import org.quartz.spi.ClassLoadHelper; import org.quartz.spi.SchedulerPlugin; /** * Logs a history of all trigger firings via the Jakarta Commons-Logging * framework. * * <p> * The logged message is customizable by setting one of the following message * properties to a String that conforms to the syntax of <code>java.util.MessageFormat</code>. * </p> * * <p> * TriggerFiredMessage - available message data are: <table> * <tr> * <th>Element</th> * <th>Data Type</th> * <th>Description</th> * </tr> * <tr> * <td>0</td> * <td>String</td> * <td>The Trigger's Name.</td> * </tr> * <tr> * <td>1</td> * <td>String</td> * <td>The Trigger's Group.</td> * </tr> * <tr> * <td>2</td> * <td>Date</td> * <td>The scheduled fire time.</td> * </tr> * <tr> * <td>3</td> * <td>Date</td> * <td>The next scheduled fire time.</td> * </tr> * <tr> * <td>4</td> * <td>Date</td> * <td>The actual fire time.</td> * </tr> * <tr> * <td>5</td> * <td>String</td> * <td>The Job's name.</td> * </tr> * <tr> * <td>6</td> * <td>String</td> * <td>The Job's group.</td> * </tr> * <tr> * <td>7</td> * <td>Integer</td> * <td>The re-fire count from the JobExecutionContext.</td> * </tr> * </table> * * The default message text is <i>"Trigger {1}.{0} fired job {6}.{5} at: {4, * date, HH:mm:ss MM/dd/yyyy}"</i> * </p> * * <p> * TriggerMisfiredMessage - available message data are: <table> * <tr> * <th>Element</th> * <th>Data Type</th> * <th>Description</th> * </tr> * <tr> * <td>0</td> * <td>String</td> * <td>The Trigger's Name.</td> * </tr> * <tr> * <td>1</td> * <td>String</td> * <td>The Trigger's Group.</td> * </tr> * <tr> * <td>2</td> * <td>Date</td> * <td>The scheduled fire time.</td> * </tr> * <tr> * <td>3</td> * <td>Date</td> * <td>The next scheduled fire time.</td> * </tr> * <tr> * <td>4</td> * <td>Date</td> * <td>The actual fire time. (the time the misfire was detected/handled)</td> * </tr> * <tr> * <td>5</td> * <td>String</td> * <td>The Job's name.</td> * </tr> * <tr> * <td>6</td> * <td>String</td> * <td>The Job's group.</td> * </tr> * </table> * * The default message text is <i>"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}"</i> * </p> * * <p> * TriggerCompleteMessage - available message data are: <table> * <tr> * <th>Element</th> * <th>Data Type</th> * <th>Description</th> * </tr> * <tr> * <td>0</td> * <td>String</td> * <td>The Trigger's Name.</td> * </tr> * <tr> * <td>1</td> * <td>String</td> * <td>The Trigger's Group.</td> * </tr> * <tr> * <td>2</td> * <td>Date</td> * <td>The scheduled fire time.</td> * </tr> * <tr> * <td>3</td> * <td>Date</td> * <td>The next scheduled fire time.</td> * </tr> * <tr> * <td>4</td> * <td>Date</td> * <td>The job completion time.</td> * </tr> * <tr> * <td>5</td> * <td>String</td> * <td>The Job's name.</td> * </tr> * <tr> * <td>6</td> * <td>String</td> * <td>The Job's group.</td> * </tr> * <tr> * <td>7</td> * <td>Integer</td> * <td>The re-fire count from the JobExecutionContext.</td> * </tr> * <tr> * <td>8</td> * <td>Integer</td> * <td>The trigger's resulting instruction code.</td> * </tr> * <tr> * <td>9</td> * <td>String</td> * <td>A human-readable translation of the trigger's resulting instruction * code.</td> * </tr> * </table> * * The default message text is <i>"Trigger {1}.{0} completed firing job * {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction * code: {9}"</i> * </p> * * @author James House */ public class LoggingTriggerHistoryPlugin implements SchedulerPlugin, TriggerListener { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Data members. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ private String name; private String triggerFiredMessage = "Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}"; 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}"; 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}"; private final Logger log = LoggerFactory.getLogger(getClass()); /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Constructors. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ public LoggingTriggerHistoryPlugin() { } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Interface. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ protected Logger getLog() { return log; } /** * Get the message that is printed upon the completion of a trigger's * firing. * * @return String */ public String getTriggerCompleteMessage() { return triggerCompleteMessage; } /** * Get the message that is printed upon a trigger's firing. * * @return String */ public String getTriggerFiredMessage() { return triggerFiredMessage; } /** * Get the message that is printed upon a trigger's mis-firing. * * @return String */ public String getTriggerMisfiredMessage() { return triggerMisfiredMessage; } /** * Set the message that is printed upon the completion of a trigger's * firing. * * @param triggerCompleteMessage * String in java.text.MessageFormat syntax. */ public void setTriggerCompleteMessage(String triggerCompleteMessage) { this.triggerCompleteMessage = triggerCompleteMessage; } /** * Set the message that is printed upon a trigger's firing. * * @param triggerFiredMessage * String in java.text.MessageFormat syntax. */ public void setTriggerFiredMessage(String triggerFiredMessage) { this.triggerFiredMessage = triggerFiredMessage; } /** * Set the message that is printed upon a trigger's firing. * * @param triggerMisfiredMessage * String in java.text.MessageFormat syntax. */ public void setTriggerMisfiredMessage(String triggerMisfiredMessage) { this.triggerMisfiredMessage = triggerMisfiredMessage; } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * SchedulerPlugin Interface. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * <p> * Called during creation of the <code>Scheduler</code> in order to give * the <code>SchedulerPlugin</code> a chance to initialize. * </p> * * @throws SchedulerConfigException * if there is an error initializing. */ public void initialize(String pname, Scheduler scheduler, ClassLoadHelper classLoadHelper) throws SchedulerException { this.name = pname; scheduler.getListenerManager().addTriggerListener(this, EverythingMatcher.allTriggers()); } public void start() { // do nothing... } /** * <p> * Called in order to inform the <code>SchedulerPlugin</code> that it * should free up all of it's resources because the scheduler is shutting * down. * </p> */ public void shutdown() { // nothing to do... } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * TriggerListener Interface. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* * Object[] arguments = { new Integer(7), new * Date(System.currentTimeMillis()), "a disturbance in the Force" }; * * String result = MessageFormat.format( "At {1,time} on {1,date}, there * was {2} on planet {0,number,integer}.", arguments); */ public String getName() { return name; } public void triggerFired(Trigger trigger, JobExecutionContext context) { if (!getLog().isInfoEnabled()) { return; } Object[] args = { trigger.getKey().getName(), trigger.getKey().getGroup(), trigger.getPreviousFireTime(), trigger.getNextFireTime(), new java.util.Date(), context.getJobDetail().getKey().getName(), context.getJobDetail().getKey().getGroup(), Integer.valueOf(context.getRefireCount()) }; getLog().info(MessageFormat.format(getTriggerFiredMessage(), args)); } public void triggerMisfired(Trigger trigger) { if (!getLog().isInfoEnabled()) { return; } Object[] args = { trigger.getKey().getName(), trigger.getKey().getGroup(), trigger.getPreviousFireTime(), trigger.getNextFireTime(), new java.util.Date(), trigger.getJobKey().getName(), trigger.getJobKey().getGroup() }; getLog().info(MessageFormat.format(getTriggerMisfiredMessage(), args)); } public void triggerComplete(Trigger trigger, JobExecutionContext context, CompletedExecutionInstruction triggerInstructionCode) { if (!getLog().isInfoEnabled()) { return; } String instrCode = "UNKNOWN"; if (triggerInstructionCode == CompletedExecutionInstruction.DELETE_TRIGGER) { instrCode = "DELETE TRIGGER"; } else if (triggerInstructionCode == CompletedExecutionInstruction.NOOP) { instrCode = "DO NOTHING"; } else if (triggerInstructionCode == CompletedExecutionInstruction.RE_EXECUTE_JOB) { instrCode = "RE-EXECUTE JOB"; } else if (triggerInstructionCode == CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_COMPLETE) { instrCode = "SET ALL OF JOB'S TRIGGERS COMPLETE"; } else if (triggerInstructionCode == CompletedExecutionInstruction.SET_TRIGGER_COMPLETE) { instrCode = "SET THIS TRIGGER COMPLETE"; } Object[] args = { trigger.getKey().getName(), trigger.getKey().getGroup(), trigger.getPreviousFireTime(), trigger.getNextFireTime(), new java.util.Date(), context.getJobDetail().getKey().getName(), context.getJobDetail().getKey().getGroup(), Integer.valueOf(context.getRefireCount()), triggerInstructionCode.toString(), instrCode }; getLog().info(MessageFormat.format(getTriggerCompleteMessage(), args)); } public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { return false; } }