quartz 学习一

     创建一个 Java 工程,引入几个 JAR 到工程中才能成功构建它们。首先,你需要 Quartz 的二进制版本,包的名字是 quartz-<version>.jar。Quartz 还需要几个第三方库;这依赖于你要用到框架的什么功能而定,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.properties 文件


文件 quartz.properties 定义了 Quartz 应用运行时行为,还包含了许多能控制 Quartz 运转的属性。这个文件应该放在classpath所指的路径下,比如我们这个java工程,就将它和下面将介绍的jobs.xml一起放在项目根目录下就是。如果不清楚就查看.classpath文件,它里面就配置了你的项目的classpath。


我们来看看最基础的 quartz.properties 文件,并讨论其中一些设置。下面是一个修剪版的 quartz.propertis文件


复制代码
#============================================================================
# Configure Main Scheduler Properties  
#============================================================================
org.quartz.scheduler.instanceName = TestScheduler
org.quartz.scheduler.instanceId = AUTO
#============================================================================
# Configure ThreadPool  
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 3
org.quartz.threadPool.threadPriority = 5
#============================================================================
# Configure JobStore  
#============================================================================
org.quartz.jobStore.misfireThreshold = 60000
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
#============================================================================
# Configure Plugins 
#============================================================================
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
org.quartz.plugin.jobInitializer.fileNames = jobs.xml
org.quartz.plugin.jobInitializer.overWriteExistingJobs = true
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
org.quartz.plugin.jobInitializer.scanInterval = 10
org.quartz.plugin.jobInitializer.wrapInUserTransaction = false
复制代码


调度器属性


第一部分有两行,分别设置调度器的实例名(instanceName) 和实例 ID (instanceId)。属性org.quartz.scheduler.instanceName 可以是你喜欢的任何字符串。它用来在用到多个调度器区分特定的调度器实例。多个调度器通常用在集群环境中。(Quartz 集群将会在第十一章,“Quartz 集群”中讨论)。现在的话,设置如下的一个字符串就行:org.quartz.scheduler.instanceName = QuartzScheduler
实际上,这也是当你没有该属性配置时的默认值。


调度器的第二个属性是 org.quartz.scheduler.instanceId。和 instaneName 属性一样,instanceId 属性也允许任何字符串。这个值必须是在所有调度器实例中是唯一的,尤其是在一个集群当中。假如你想 Quartz 帮你生成这个值的话,可以设置为 AUTO。如果Quartz 框架是运行在非集群环境中,那么自动产生的值将会是 NON_CLUSTERED。假如是在集群环境下使用 Quartz,这个值将会是主机名加上当前的日期和时间。大多情况下,设置为 AUTO 即可。
·线程池属性


接下来的部分是设置有关线程必要的属性值,这些线程在 Quartz 中是运行在后台担当重任的。threadCount 属性控制了多少个工作者线程被创建用来处理 Job。原则上是,要处理的 Job 越多,那么需要的工作者线程也就越多。threadCount 的数值至少为 1。Quartz没有限定你设置工作者线程的最大值,但是在多数机器上设置该值超过100的话就会显得相当不实用了,特别是在你的 Job 执行时间较长的情况下。这项没有默认值,所以你必须为这个属性设定一个值。


threadPriority 属性设置工作者线程的优先级。优先级别高的线程比级别低的线程更优先得到执行。threadPriority 属性的最大值是常量 java.lang.Thread.MAX_PRIORITY,等于10。最小值为常量 java.lang.Thread.MIN_PRIORITY,为1。这个属性的正常值是Thread.NORM_PRIORITY,为5。大多情况下,把它设置为5,这也是没指定该属性的默认值。


最后一个要设置的线程池属性是 org.quartz.threadPool.class。这个值是一个实现了 org.quartz.spi.ThreadPool 接口的类的全限名称。Quartz 自带的线程池实现类是 org.quartz.smpl.SimpleThreadPool,它能够满足大多数用户的需求。这个线程池实现具备简单的行为,并经很好的测试过。它在调度器的生命周期中提供固定大小的线程池。你能根据需求创建自己的线程池实现,如果你想要一个随需可伸缩的线程池时也许需要这么做。这个属性没有默认值,你必须为其指定值。



作业存储设置


作业存储部分的设置描述了在调度器实例的生命周期中,Job 和 Trigger 信息是如何被存储的。我们还没有谈论到作业存储和它的目的;因为对当前例子是非必的,所以我们留待以后说明。现在的话,你所要了解的就是我们存储调度器信息在内存中而不是在关系型数据库中就行了。


把调度器信息存储在内存中非常的快也易于配置。当调度器进程一旦被终止,所有的 Job 和 Trigger 的状态就丢失了。要使 Job 存储在内存中需通过设置  org.quartz.jobStrore.class 属性为 org.quartz.simpl.RAMJobStore。假如我们不希望在 JVM 退出之后丢失调度器的状态信息的话,我们可以使用关系型数据库来存储这些信息。这需要另一个作业存储(JobStore) 实现,我们在后面将会讨论到。第五章“Cron Trigger 和其他”和第六章“作业存储和持久化”会提到你需要用到的不同类型的作业存储实现。
·插件配置


在这个简单的 quartz.properties 文件中最后一部分是你要用到的 Quart 插件的配置。插件常常在别的开源框架上使用到,比如 Apache 的 Struts 框架(见 http://struts.apache.org/)。


一个声明式扩框架的方法就是通过新加实现了 org.quartz.spi.SchedulerPlugin 接口的类。SchedulerPlugin  接口中有给调度器调用的三个方法。


要在我们的例子中声明式配置调度器信息,我们会用到一个 Quartz 自带的叫做 org.quartz.plugins.xml.JobInitializationPlugin 的插件。


默认时,这个插件会在 classpath 中搜索名为 quartz_jobs.xml 的文件并从中加载 Job 和 Trigger 信息。在下下面中讨论 quartz_jobs.xml 文件,这是我们所参考的非正式的 Job 定义文件。


·为插件修改 quartz.properties 配置


JobInitializationPlugin 找寻 quartz_jobs.xml 来获得声明的 Job 信息。假如你想改变这个文件名,你需要修改 quartz.properties 来告诉插件去加载那个文件。例如,假如你想要 Quartz 从名为 my_quartz_jobs.xml 的 XML 文件中加载 Job 信息,你不得不为插件指定这一文件


复制代码
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
org.quartz.plugin.jobInitializer.fileNames = jobs.xml
org.quartz.plugin.jobInitializer.overWriteExistingJobs = true
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
org.quartz.plugin.jobInitializer.scanInterval = 10
org.quartz.plugin.jobInitializer.wrapInUserTransaction = false
复制代码
我们添加了属性 org.quartz.plugin.jobInitializer.fileName 并设置该属性值为我们想要的文件名。这个文件名要对 classloader 可见,也就是说要在 classpath 下。


当 Quartz 启动后读取 quartz.properties 文件,然后初始化插件。它会传递上面配置的所有属性给插件,这时候插件也就得到通知去搜寻不同的文件。


下面就是目录扫描例子的 Job 定义的 XML 文件。正如上一篇所示例子那样,这里我们用的是声明式途径来配置 Job 和 Trigger 信息的


复制代码
<?xml version='1.0' encoding='utf-8'?>
<quartz xmlns="http://www.opensymphony.com/quartz/JobSchedulingData"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.opensymphony.com/quartz/JobSchedulingData
  http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd"
  version="1.5">   
  <job>      
    <job-detail>      
     <name>ScanDirectory</name>      
     <group>DEFAULT</group>      
     <description>      
          A job that scans a directory for files       
     </description>      
     <job-class>      
            com.vista.quartz.ScanDirectoryJob       
     </job-class>      
     <volatility>false</volatility>      
     <durability>false</durability>      
     <recover>false</recover>      
     <job-data-map allows-transient-data="true">      
         <entry>      
         <key>SCAN_DIR</key>      
         <value>D:\conf1</value>      
       </entry>      
     </job-data-map>      
    </job-detail>      
      
    <trigger>      
     <simple>      
       <name>scanTrigger</name>      
       <group>DEFAULT</group>      
       <job-name>ScanDirectory</job-name>      
       <job-group>DEFAULT</job-group>      
       <start-time>2008-09-03T14:43:00</start-time>      
       <!-- repeat indefinitely every 10 seconds -->      
       <repeat-count>-1</repeat-count>      
       <repeat-interval>10000</repeat-interval>      
     </simple>      
    </trigger>      
  </job>      
</quartz>
复制代码
在jobs.xml 中 <start-time> 的格式是:


<start-time>2008-09-03T14:43:00</start-time>
其中T隔开日期和时间,默认时区 
或者:


<start-time>2008-09-03T14:43:00+08:00</start-time> 
其中+08:00 表示东八区


<job> 元素描述了一个要注册到调度器上的 Job,相当于我们在前面章节中使用 scheduleJob() 方法那样。你所看到的<job-detail> 和  <trigger> 这两个元素就是我们在代码中以编程式传递给方法 schedulerJob() 的参数。前面本质上是与这里一样的,只是现在用的是一种较流行声明的方式。<trigger>元素也是非常直观的:它使用前面同样的属性,但更简单的建立一个 SimpleTrigger。因此仅仅是一种不同的(可论证的且更好的)方式做了上一篇代码 中同样的事情。显然,你也可以支持多个 Job。在上一篇代码 中我们编程的方式那么做的,也能用声明的方式来支持


复制代码


<?xml version='1.0' encoding='utf-8'?>
<quartz xmlns="http://www.opensymphony.com/quartz/JobSchedulingData"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.opensymphony.com/quartz/JobSchedulingData
  http://www.opensymphony.com/quartz/xml/job_scheduling_data_1_5.xsd"
  version="1.5">   
 <job>      
    <job-detail>      
     <name>ScanDirectory1</name>      
     <group>DEFAULT</group>      
     <description>      
           A job that scans a directory for files       
     </description>      
     <job-class>      
            com.vista.quartz.ScanDirectoryJob       
     </job-class>      
     <volatility>false</volatility>      
     <durability>false</durability>      
     <recover>false</recover>      
      
     <job-data-map allows-transient-data="true">      
     <entry>      
       <key>SCAN_DIR</key>      
         <value>D:\dyk\Java\Tomcat\conf</value>      
     </entry>      
    </job-data-map>      
  </job-detail>      
  <trigger>      
    <simple>      
     <name>scanTrigger1</name>      
     <group>DEFAULT</group>      
     <job-name>ScanDirectory1</job-name>      
     <job-group>DEFAULT</job-group>      
     <start-time>2008-09-03T15:00:10</start-time>      
     <!-- repeat indefinitely every 10 seconds -->      
     <repeat-count>-1</repeat-count>      
     <repeat-interval>10000</repeat-interval>      
    </simple>      
  </trigger>      
</job>       
<job>      
  <job-detail>      
    <name>ScanDirectory2</name>      
    <group>DEFAULT</group>      
    <description>      
          A job that scans a directory for files       
    </description>      
    <job-class>      
          com.vista.quartz.ScanDirectoryJob       
    </job-class>      
    <volatility>false</volatility>      
    <durability>false</durability>      
    <recover>false</recover>      
    <job-data-map allows-transient-data="true">      
      <entry>      
       <key>SCAN_DIR</key>      
       <value>D:\dyk\Java\Tomcat\webapps\MyTest\WEB-INF</value>      
     </entry>      
    </job-data-map>      
  </job-detail>      
  <trigger>      
    <simple>      
     <name>scanTrigger2</name>      
     <group>DEFAULT</group>      
     <job-name>ScanDirectory2</job-name>      
     <job-group>DEFAULT</job-group>      
     <start-time>2008-09-03T15:00:20</start-time>      
     <!-- repeat indefinitely every 15 seconds -->      
     <repeat-count>-1</repeat-count>      
     <repeat-interval>15000</repeat-interval>      
    </simple>      
  </trigger>      
 </job>   
</quartz>
复制代码
最后我们来看看原来的代码简化成如何了:


复制代码
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());         
        } 
        catch (SchedulerException ex)
        {       
             logger.error(ex);       
        }       
    }       
    public Scheduler createScheduler() throws SchedulerException 
    {//创建调度器       
        return StdSchedulerFactory.getDefaultScheduler();
    }   
}
转自: http://www.cnblogs.com/phinecos/archive/2008/09/03/1282747.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值