jfinal使用QuartzPlugin(定时任务插件)(上)

jfinal扩展的定时任务插件。

maven依赖:

<dependency>
    <groupId>com.jfinal</groupId>
    <artifactId>jfinal</artifactId>
    <version>4.1</version>
</dependency>
<dependency>
    <groupId>com.jfinal</groupId>
    <artifactId>jfinal-undertow</artifactId>
    <version>1.6</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.1</version>
</dependency>
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>

日志配置: log4j.properties

### 设置###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = logs/debug.log
log4j.appender.R.DatePattern = '.'yyyy-MM-dd
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =logs/error.log
log4j.appender.E.DatePattern = '.'yyyy-MM-dd
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

QuartzPlugin源码:

import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;
import com.jfinal.plugin.IPlugin;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.Date;
import java.util.Map;
import java.util.Set;

public class QuartzPlugin implements IPlugin {
    public static final String VERSION_1 = "1";
    private static final String JOB = "job";
    private final Log logger = Log.getLog(getClass());
    private Map<Job, String> jobs = Maps.newLinkedHashMap();
    private String version;
    private SchedulerFactory sf;
    private Scheduler scheduler;
    private String jobConfig;
    private String confConfig;
    private Map<String, String> jobProp;

    public QuartzPlugin(String jobConfig, String confConfig) {
        this.jobConfig = jobConfig;
        this.confConfig = confConfig;
    }

    public QuartzPlugin(String jobConfig) {
        this.jobConfig = jobConfig;
    }

    public QuartzPlugin() {
    }

    public QuartzPlugin add(String jobCronExp, Job job) {
        jobs.put(job, jobCronExp);
        return this;
    }

    @Override
    public boolean start() {
        loadJobsFromProperties();
        startJobs();
        return true;
    }

    private void startJobs() {
        try {
            if (StrKit.notBlank(confConfig)) {
                sf = new StdSchedulerFactory(confConfig);
            } else {
                sf = new StdSchedulerFactory();
            }
            scheduler = sf.getScheduler();
        } catch (SchedulerException e) {
            Throwables.propagate(e);
        }
        Set<Map.Entry<Job, String>> set = jobs.entrySet();
        for (Map.Entry<Job, String> entry : set) {
            Job job = entry.getKey();
            String jobClassName = job.getClass().getName();
            String jobCronExp = entry.getValue();
            JobDetail jobDetail;
            CronTrigger trigger;
            //JobDetail and CornTrigger are classes in 1.x version,but are interfaces in 2.X version.
            if (VERSION_1.equals(version)) {
                jobDetail = Reflect.on("org.quartz.JobDetail").create(jobClassName, jobClassName, job.getClass()).get();
                trigger = Reflect.on("org.quartz.CronTrigger").create(jobClassName, jobClassName, jobCronExp).get();
            } else {
                jobDetail = Reflect.on("org.quartz.JobBuilder").call("newJob", job.getClass()).call("withIdentity", jobClassName, jobClassName)
                        .call("build").get();
                Object temp = Reflect.on("org.quartz.TriggerBuilder").call("newTrigger").get();
                temp = Reflect.on(temp).call("withIdentity", jobClassName, jobClassName).get();
                CronScheduleBuilder csb = Reflect.on("org.quartz.CronScheduleBuilder").call("cronSchedule", jobCronExp).get();
                temp = Reflect.on(temp).call("withSchedule", csb).get();
                trigger = Reflect.on(temp).call("build").get();
            }
            Date ft = Reflect.on(scheduler).call("scheduleJob", jobDetail, trigger).get();
            logger.debug(Reflect.on(jobDetail).call("getKey") + " has been scheduled to run at: " + ft + " " +
                    "and repeat based on expression: " + Reflect.on(trigger).call("getCronExpression"));
        }
        try {
            scheduler.start();
        } catch (SchedulerException e) {
            Throwables.propagate(e);
        }
    }

    private void loadJobsFromProperties() {
        if (StrKit.isBlank(jobConfig)) {
            return;
        }
        jobProp = ResourceKit.readProperties(jobConfig);
        Set<Map.Entry<String, String>> entries = jobProp.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            if (!key.endsWith(JOB) || !isEnableJob(enable(key))) {
                continue;
            }
            String jobClassName = jobProp.get(key) + "";
            String jobCronExp = jobProp.get(cronKey(key)) + "";
            Class<Job> job = Reflect.on(jobClassName).get();
            try {
                jobs.put(job.newInstance(), jobCronExp);
            } catch (Exception e) {
                Throwables.propagate(e);
            }
        }
    }

    private String enable(String key) {
        return key.substring(0, key.lastIndexOf(JOB)) + "enable";
    }

    private String cronKey(String key) {
        return key.substring(0, key.lastIndexOf(JOB)) + "cron";
    }

    public QuartzPlugin version(String version) {
        this.version = version;
        return this;
    }

    private boolean isEnableJob(String enableKey) {
        Object enable = jobProp.get(enableKey);
        if (enable != null && "false".equalsIgnoreCase((enable + "").trim())) {
            return false;
        }
        return true;
    }

    @Override
    public boolean stop() {
        try {
            scheduler.shutdown();
        } catch (SchedulerException e) {
            Throwables.propagate(e);
        }
        return true;
    }

    public QuartzPlugin confConfig(String confConfig) {
        this.confConfig = confConfig;
        return this;
    }

    public QuartzPlugin jobConfig(String jobConfig) {
        this.jobConfig = jobConfig;
        return this;
    }
}


评论区

l745230

2019-05-31 14:02

代码看着眼熟,这是jfinal-ext里的吧

hamy

2019-05-31 14:02

与Cron4jPlugin相比,有哪些优点呢

l745230

2019-05-31 14:02

多引入了com.google.guava,要是能修改下,把guava去掉就更好了

zhangshiqiang

2019-05-31 14:03

@l745230 是的 jfinal-ext2 复制过来的

zhangshiqiang

2019-05-31 14:03

@hamy 支持秒级别

zhangshiqiang

2019-05-31 14:03

l745230

2019-05-31 14:05

jfinal-ext的这个定时任务很稳,跑了4,5个项目了,没出过问题

xiuj

2019-05-31 14:11

支持秒级别

zlsj80

2019-05-31 15:45

有没有弄成html页面来配置

JFinal

2019-05-31 21:38

有不少同学需要精确到秒的任务调度,这种情况下 quartz 似乎是必然的选择,谢谢你的分享

fmpoffice

2019-06-10 09:11

请问 ResourceKit 依赖的那个包?

zhangshiqiang

2019-06-10 18:02

@fmpoffice 谷歌的那个包 guava

dplxm

2019-10-10 17:18

@zhangshiqiang 谷歌的包 guava 里面没有ResourceKit 我导入依赖也没有ResourceKit这个类

zhangshiqiang

2019-10-24 17:33

@dplxm https://github.com/OpeningO/JFinal-ext2.git 源码地址

一个初学者

2019-12-25 16:14

同问有没有尝试过,通过页面来对定时任务进行新增修改删除等内容