Quartz 框架快速入門(一)

      創建一個 Java 工程,引入幾個 JAR 到工程中才能成功構建它們。首先,你需要 Quartz 的二進制版本,包的名字是 quartz-<version>.jarQuartz 還需要幾個第三方庫;這依賴於你要用到框架的什麼功能而定,Commons Digester 庫可以在 <QUARTZ_HOME>/lib/core <QUARTZ_HOME>/lib/optional 目錄中找到。如果出現java.lang.NoClassDefFoundError: javax/transaction/UserTransaction的錯誤,解決辦法是:引入jta.jar包,這個包在quartz-1.6.0/lib/build 下。

·創建一個 Quartz Job

每一個 Quartz Job 必須有一個實現了 org.quartz.Job 接口的具體類。這個接口僅有一個要你在 Job 中實現的方法,execute(),方法 execute() 的原型如下:
public void execute(JobExecutionContext context) throws JobExecutionException;

Quartz 調度器確定到時間要激發一個 Job 的時候,它就會生成一個 Job 實例,並調用這個實例的 execute() 方法。調度器只管調用 execute() 方法,而不關心執行的結果,除了在作業執行中出問題拋出的 org.quartz.JobExecutionException 異常。

下面是我們的第一個 Quartz job,它被設計來掃描一個目錄中的文並顯示文件的詳細信息。

複製代碼

package com.vista.quartz;
import java.io.File;       
import java.io.FileFilter;
import java.util.Date;       
      

import org.apache.commons.logging.Log;       
import org.apache.commons.logging.LogFactory;       
import org.quartz.Job;       
import org.quartz.JobDataMap;       
import org.quartz.JobDetail;       
import org.quartz.JobExecutionContext;       
import org.quartz.JobExecutionException;  

public class ScanDirectoryJob implements Job 
{
    
static Log logger = LogFactory.getLog(ScanDirectoryJob.class);//日誌記錄器
    
    
public void execute(JobExecutionContext context) throws JobExecutionException 
    {
        
//Every job has its own job detail       
        JobDetail jobDetail = context.getJobDetail();       
        
// The name is defined in the job definition       
        String jobName = jobDetail.getName();//任務名稱       
        
// Log the time the job started       
        logger.info(jobName + " fired at " + new Date());//記錄任務開始執行的時間       
        
// The directory to scan is stored in the job map       
        JobDataMap dataMap = jobDetail.getJobDataMap();//任務所配置的數據映射表       
        String dirName = dataMap.getString("SCAN_DIR");//獲取要掃描的目錄       
        
// Validate the required input       
        if (dirName == null
        {
//所需要的掃描目錄沒有提供       
             throw new JobExecutionException( "Directory not configured" );       
        }       
        
// Make sure the directory exists       
        File dir = new File(dirName);       
        
if (!dir.exists()) 
        {
//提供的是錯誤目錄       
            throw new JobExecutionException( "Invalid Dir "+ dirName);       
        }       
        
// Use FileFilter to get only XML files       
        FileFilter filter = new FileExtensionFileFilter(".xml");       
        
//只統計xml文件
        File[] files = dir.listFiles(filter);       
        
if (files == null || files.length <= 0
        {
//目錄下沒有xml文件       
            logger.info("No XML files found in " + dir);       
            
// Return since there were no files       
            return;
        }       
        
// The number of XML files       
        int size = files.length;          
        
// Iterate through the files found       
        for (int i = 0; i < size; i++
        {
            File file 
= files[i];       
            
// Log something interesting about each file.       
            File aFile = file.getAbsoluteFile();       
            
long fileSize = file.length();       
            String msg 
= aFile + " - Size: " + fileSize;       
            logger.info(msg);
//記錄下文件的路徑和大小
        } 
    }
}

複製代碼

     當 Quartz 調用 execute() 方法,會傳遞一個 org.quartz.JobExecutionContext 上下文變量,裏面封裝有 Quartz 的運行時環境和當前正執行的 Job。通過 JobexecutionContext,你可以訪問到調度器的信息,作業和作業上的觸發器的信息,還有更多更多的信息。在代碼中,JobExecutionContext 被用來訪問 org.quartz.JobDetail 類,JobDetail 類持有 Job 的詳細信息,包括爲 Job 實例指定的名稱,Job 所屬組,Job 是否被持久化(易失性),和許多其他感興趣的屬性。

JobDetail 又持有一個指向 org.quartz.JobDataMap 的引用。JobDataMap 中有爲指定 Job 配置的自定義屬性。例如,在代碼中我們從 JobDataMap 中獲得欲掃描的目錄名,我們可以在 ScanDirectoryJob 中硬編碼這個目錄名,但是這樣的話我們難以重用這個 Job 來掃描別的目錄了。在後面你將會看到目錄是如何配置到 JobDataMap 的。

execute() 方法中剩下的就是標準 Java 代碼了:獲得目錄名並創建一個 java.io.File 對象。它還對目錄名作爲簡單的校驗,確保是一個有效且存在的目錄。接着調用 File 對象的 listFiles() 方法得到目錄下的文件。還創建了一個 java.io.FileFilter 對象作爲參數傳遞給 listFiles() 方法。org.quartzbook.cavaness.FileExtensionFileFilter 實現了 java.io.FileFilter 接口,它的作用是過濾掉目錄僅返回 XML 文件。默認情況下,listFiles() 方法是返回目錄中所有內容,不管是文件還是子目錄,所以我們必須過濾一下,因爲我們只對 XML 文件感興趣。

FileExtensionFileFilter 被用來屏蔽名稱中不含字符串 “.xml” 的文件。它還屏蔽了子目錄--這些子目錄原本會讓 listFiles() 方法正常返回。過濾器提供了一種很便利的方式選擇性的向你的 Quartz 作業提供它能接受的作爲輸入的文件

複製代碼

package com.vista.quartz;
import  java.io.File;       
import  java.io.FileFilter;   

public class FileExtensionFileFilter implements  FileFilter 
{
     
private  String extension;//文件後綴     
     
     
public  FileExtensionFileFilter(String extension)
     {       
         
this.extension = extension;
     }       
      
     
public   boolean  accept(File file)
     {
//只接受指定後綴的文件       
         
// Lowercase the filename for easier comparison       
         String lCaseFilename = file.getName().toLowerCase();//小寫化
         return  (file.isFile() &&(lCaseFilename.indexOf(extension) >  0 )) ?  true : false ;       
     }       
}

複製代碼

到目前爲止,我們已經創建了一個 Quartz job,但還沒有決定怎麼處置它--明顯地,我們需以某種方式爲這個 Job 設置一個運行時間表。時間表可以是一次性的事件,或者我們可能會安裝它在除週日之外的每個午夜執行。你即刻將會看到,Quartz Schduler 是框架的心臟與靈魂。所有的 Job 都通過 Schduler 註冊;必要時,Scheduler 也會創建 Job 類的實例,並執行實例的 execute() 方法。

·編程式安排一個 Quartz Job

所有的要 Quartz 來執行的作業必須通過調度器來註冊。大多情況下,這會在調度器啓動前做好。正如前面說過,這一操作也提供了聲明式與編程式兩種實現途徑的選擇。

因爲每一個 Job 都必須用 Scheduler 來註冊,所以先定義一個 JobDetail,並關聯到這個 Scheduler 實例。

下面的程序提供了一個理解如何編程式安排一個 Job 很好的例子。代碼首先調用 createScheduler() 方法從 Scheduler 工廠獲取一個 Scheduler 的實例。得到 Scheduler 實例之後,把它傳遞給 schedulerJob() 方法,由它把 Job 同 Scheduler 進行關聯。

首先,創建了我們想要運行的 Job 的 JobDetail 對象。JobDetail 構造器的參數中包含指派給 Job 的名稱,邏輯組名,和實現 org.quartz.Job 接口的全限類名稱。我們可以使用 JobDetail 的別的構造器。

在前面有說過,JobDetail 扮演着某一 Job 定義的角色。它帶有 Job 實例的屬性,能在運行時被所關聯的 Job 訪問到。其中在使用 JobDetail 時,的一個最重要的東西就是 JobDataMap,它被用來存放 Job 實例的狀態和參數。在代碼中,待掃描的目錄名稱就是通過  scheduleJob() 方法存入到 JobDataMap 中的。

Job 只是一個部分而已。注意我們沒有在 JobDetail 對象中爲 Job 設定執行日期和次數。這是 Quartz Trigger 該做的事。顧名思義,Trigger 的責任就是觸發一個 Job 去執行。當用 Scheduler 註冊一個 Job 的時候要創建一個 Trigger 與這個 Job 相關聯。Quartz 提供了四種類型的 Trigger,但其中兩種是最爲常用的,它們就是在下面要用到的 SimpleTrigger 和  CronTrigger.

SimpleTrigger 是兩個之中簡單的那個,它主要用來激發單事件的 Job,Trigger 在指定時間激發,並重復 n 次--兩次激發時間之間的延時爲 m,然後結束作業。CronTrigger 非常複雜且強大。它是基於通用的公曆,當需要用一種較複雜的時間表去執行一個 Job 時用到。例如,四月至九月的每個星期一、星期三、或星期五的午夜。

爲更簡單的使用 Trigger,Quartz 包含了一個工具類,叫做 org.quartz.TriggerUtils. TriggerUtils 提供了許多便捷的方法簡化了構造和配置 trigger. 本文的例子中有用的就是 TriggerUtils 類;SimpleTriggerCronTrigger 會在後面用到。

正如你看到的那樣,調用了 TriggerUtils 的方法 makeSecondlyTrigger() 來創建一個每10秒種激發一次的 trigger(實際是由 TriggerUtils 生成了一個 SimpleTrigger 實例,但是我們的代碼並不想知道這些)。我們同樣要給這個 trigger 實例一個名稱並告訴它何時激發相應的 Job;與之關聯的 Job 會立即啓動,因爲由方法 setStartTime() 設定的是當前時間

複製代碼

package com.vista.quartz;

import java.util.Date;       

import org.apache.commons.logging.Log;       
import org.apache.commons.logging.LogFactory;       
import org.quartz.JobDetail;
import org.quartz.Scheduler;       
import org.quartz.SchedulerException;       
import org.quartz.Trigger;
import org.quartz.TriggerUtils;
import org.quartz.impl.StdSchedulerFactory;

public class SimpleScheduler 
{
    
static Log logger = LogFactory.getLog(SimpleScheduler.class);         
    
public static void main(String[] args) 
    {       
         SimpleScheduler simple 
= new SimpleScheduler();       
         
try
         {       
             
// Create a Scheduler and schedule the Job       
             Scheduler scheduler = simple.createScheduler();       
             simple.scheduleJob(scheduler);       
    
             
// Start the Scheduler running       
             scheduler.start();       
    
             logger.info( 
"Scheduler started at " + new Date());       
    
        } 
catch (SchedulerException ex) {       
             logger.error(ex);       
        }   
        
    }       
    
public Scheduler createScheduler() throws SchedulerException 
    {
//創建調度器       
        return StdSchedulerFactory.getDefaultScheduler();
    }   
    
    
//Create and Schedule a ScanDirectoryJob with the Scheduler       
    private void scheduleJob(Scheduler scheduler) throws SchedulerException 
    {       
         
// Create a JobDetail for the Job       
         JobDetail jobDetail = new JobDetail("ScanDirectory",Scheduler.DEFAULT_GROUP,ScanDirectoryJob.class); 
         
// Configure the directory to scan       
         jobDetail.getJobDataMap().put("SCAN_DIR","D:\\Tomcat\\conf"); //set the JobDataMap that is associated with the Job.            
         
// Create a trigger that fires every 10 seconds, forever       
         Trigger trigger = TriggerUtils.makeSecondlyTrigger(10);//每10秒觸發一次       
         trigger.setName("scanTrigger");       
         
// Start the trigger firing from now       
         trigger.setStartTime(new Date());//設置第一次觸發時間            
         
// Associate the trigger with the job in the scheduler       
         scheduler.scheduleJob(jobDetail, trigger);       
    }       
}

複製代碼

     假如你有不只一個個 Job (你也許就是),你將需要爲每一個 Job 創建各自的 JobDetail。每一個 JobDetail 必須通過 scheduleJob() 方法一一註冊到 Scheduler 上。而如果你想重用了一個 Job 類,讓它產生多個實例運行,那麼你需要爲每個實例都創建一個 JobDetail。例如,假如你想重用 ScanDirectoryJob 讓它檢查兩個不同的目錄,你需要創建並註冊兩個 JobDetail 實例

複製代碼

package com.vista.quartz;

import java.util.Date;       

import org.apache.commons.logging.Log;       
import org.apache.commons.logging.LogFactory;       
import org.quartz.JobDetail;
import org.quartz.Scheduler;       
import org.quartz.SchedulerException;       
import org.quartz.Trigger;
import org.quartz.TriggerUtils;
import org.quartz.impl.StdSchedulerFactory;

public class SimpleScheduler 
{
    
static Log logger = LogFactory.getLog(SimpleScheduler.class);         
    
public static void main(String[] args) 
    {       
         SimpleScheduler simple 
= new SimpleScheduler();       
         
try 
         {       
             
// Create a Scheduler and schedule the Job       
             Scheduler scheduler = simple.createScheduler();         
             
// Jobs can be scheduled after Scheduler is running       
             scheduler.start();          
             logger.info(
"Scheduler started at " + new Date());         
             
// Schedule the first Job       
             simple.scheduleJob(scheduler, "ScanDirectory1",ScanDirectoryJob.class,"D:\\conf1"10);          
             
// Schedule the second Job       
             simple.scheduleJob(scheduler, "ScanDirectory2",ScanDirectoryJob.class,"D:\\conf2 "15);       
        } 
        
catch (SchedulerException ex)
        {       
             logger.error(ex);       
        }       
    }       
    
public Scheduler createScheduler() throws SchedulerException 
    {
//創建調度器       
        return StdSchedulerFactory.getDefaultScheduler();
    }   
    
    
private void scheduleJob(Scheduler scheduler, String jobName,Class jobClass, String scanDir, int scanInterval) throws SchedulerException 
    {         
        
// Create a JobDetail for the Job       
       JobDetail jobDetail = new JobDetail(jobName,Scheduler.DEFAULT_GROUP, jobClass);         
       
// Configure the directory to scan       
       jobDetail.getJobDataMap().put("SCAN_DIR", scanDir);         
       
// Trigger that repeats every "scanInterval" secs forever       
       Trigger trigger = TriggerUtils.makeSecondlyTrigger(scanInterval);         
       trigger.setName(jobName 
+ "-Trigger");         
       
// Start the trigger firing from now       
       trigger.setStartTime(new Date());         
       
// Associate the trigger with the job in the scheduler       
       scheduler.scheduleJob(jobDetail, trigger);       
  }       
}

複製代碼

 

 

作者:洞庭散人

出處:http://phinecos.cnblogs.com/    

本博客遵從Creative Commons Attribution 3.0 License,若用於非商業目的,您可以自由轉載,但請保留原作者信息和文章鏈接URL。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章