Quartz自定义Trigger类型 WeekdaysTrigger

quartz是一个可以动态添加、管理定时任务的框架,因为业务需要,增加一种支持周XX执行的定时任务WeekdaysTrigger。

实现思路如下:首先定义WeekdaysTrigger接口:

public interface WeekdaysTrigger extends Trigger {

    
    // 仅支持两种misfire补偿策略:忽略与立即执行 
    int MISFIRE_INSTRUCTION_FIRE_NOW = 1;

    int getTimesTriggered();

    void setTimesTriggered(int timesTriggered);

    // 需要执行的周XX
    Integer[] getWeekdays();

    // 触发的时间点
    String getTriggerTime();

    // 定时器开始时间
    Date getInitTime();

    String getOtherParams();
}

接着定义WeekdaysTrigger的实现WeekdaysTriggerImpl

@Slf4j
public class WeekdaysTriggerImpl extends AbstractTrigger<WeekdaysTrigger> implements WeekdaysTrigger, CoreTrigger {

    private static final int YEAR_TO_GIVEUP_SCHEDULING_AT = java.util.Calendar.getInstance().get(java.util.Calendar.YEAR) + 100;
//    public static final int REPEAT_INDEFINITELY = -1;

    protected Integer[] weekdays;

    protected Date initTime;

    // 定时器加载时的首次触发时间
    protected Date startTime;

    protected String triggerTime;

    private int timesTriggered;

    // 上一次触发时间、下一次触发时间
    private Date previousFireTime, nextFireTime;

    protected WeekdaysTriggerImpl(){}

    /**
     *
     * @param weekdays 星期 x
     * @param triggerTime 每日的触发时间点 HH:mm
     * @param startTime 触发器开始时间
     */
    public WeekdaysTriggerImpl(Integer weekdays[],
                               String triggerTime,
                                   Date startTime) {
        this.weekdays = weekdays;
        this.triggerTime = triggerTime;
        this.initTime = startTime;
        try {
            this.startTime = this.getNextFireTime(startTime);
        } catch (ParseException e) {
            e.printStackTrace();
            log.error("WeekdaysTrigger 创建失败", e);
        }
    }

    // 计算下一次触发时间
    protected Date getNextFireTime(Date startTime) throws ParseException {
        Date now = new Date();
        Date sameDayTime = DateUtil.appendTime2Date(startTime, triggerTime);
        if(now.compareTo(sameDayTime) > 0 || !judgeWeekdayLegal(startTime)) { // 如果当天时间已过 或者 非有效的星期x
            Date nextLegalDate = null;

            if(now.compareTo(startTime) > 0) { // 跳过无效的天
                nextLegalDate = getNextLegalDate(now);
            } else {
                nextLegalDate = getNextLegalDate(startTime);
            }
            return DateUtil.appendTime2Date(nextLegalDate, triggerTime);
        }
        return sameDayTime;
    }

    /**
     * 星期 X 的条件是否满足
     * @param date
     * @return
     */
    protected Boolean judgeWeekdayLegal(Date date) {
        int weekday = DateUtil.getWeekday(date);
        return isWeekdayLegal(weekday);
    }


    /**
     * 获取下一个满足星期条件的日期
     * @param date
     * @return
     */
    protected Date getNextLegalDate(Date date) {
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(java.util.Calendar.DATE, 1);

        int weekday = calendar.get(java.util.Calendar.DAY_OF_WEEK);
        while(!isWeekdayLegal(weekday)) {
            calendar.add(java.util.Calendar.DATE, 1);
            weekday = calendar.get(java.util.Calendar.DAY_OF_WEEK);
        }
        return calendar.getTime();
    }

    protected Boolean isWeekdayLegal(int weekday) {
        for(Integer day: weekdays) {
            if(day.intValue() == weekday) {
                return true;
            }
        }
        return false;
    }

    // 触发之后,马上计算下一次的触发时间
    @Override
    public void triggered(Calendar calendar) {
        timesTriggered++;
        previousFireTime = nextFireTime;
        nextFireTime = getFireTimeAfter(nextFireTime);

        while (nextFireTime != null && calendar != null
                && !calendar.isTimeIncluded(nextFireTime.getTime())) {

            nextFireTime = getFireTimeAfter(nextFireTime);

            if(nextFireTime == null)
                break;

            //avoid infinite loop
            java.util.Calendar c = java.util.Calendar.getInstance();
            c.setTime(nextFireTime);
            if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
                nextFireTime = null;
            }
        }
    }

    @Override
    public Date computeFirstFireTime(Calendar calendar) {
        nextFireTime = getStartTime();

        while (nextFireTime != null && calendar != null
                && !calendar.isTimeIncluded(nextFireTime.getTime())) {
            nextFireTime = getFireTimeAfter(nextFireTime);

            if(nextFireTime == null)
                break;

            //avoid infinite loop
            java.util.Calendar c = java.util.Calendar.getInstance();
            c.setTime(nextFireTime);
            if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
                return null;
            }
        }

        return nextFireTime;
    }

    @Override
    public boolean mayFireAgain() {
        return (this.getNextFireTime() != null);
    }

    @Override
    public Date getStartTime() {
        return this.startTime;
    }

    @Override
    public void setStartTime(Date startTime) {
    }

    @Override
    public void setEndTime(Date endTime) {

    }

    @Override
    public Date getEndTime() {
        return null;
    }

    @Override
    public Date getNextFireTime() {
        return nextFireTime;
    }

    @Override
    public Date getPreviousFireTime() {
        return previousFireTime;
    }

    @Override
    public Date getFireTimeAfter(Date afterTime) {
        java.util.Calendar calendar = java.util.Calendar.getInstance();
        calendar.setTime(afterTime);
        calendar.add(java.util.Calendar.DATE, 1);
        try {
            return this.getNextFireTime(calendar.getTime());
        } catch (ParseException e) {
            log.error(e.getMessage() , e);
            return null;
        }
    }

    @Override
    public Date getFinalFireTime() {
        return null;
//        return (getEndTime() == null) ? null : getFireTimeBefore(getEndTime());
    }

    @Override
    protected boolean validateMisfireInstruction(int misfireInstruction) {
        if (misfireInstruction < MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY) {
            return false;
        }

        if (misfireInstruction > MISFIRE_INSTRUCTION_FIRE_NOW) {
            return false;
        }

        return true;
    }

    // 目前支持 ignore 和 now 两种misfire处理方式
    @Override
    public void updateAfterMisfire(Calendar cal) {
        int instr = getMisfireInstruction();

        
        if(instr == Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY)
            return;

        setNextFireTime(new Date());
    }

    @Override
    public void updateWithNewCalendar(Calendar calendar, long misfireThreshold) {
        nextFireTime = getFireTimeAfter(previousFireTime);

        if (nextFireTime == null || calendar == null) {
            return;
        }

        Date now = new Date();
        while (nextFireTime != null && !calendar.isTimeIncluded(nextFireTime.getTime())) {

            nextFireTime = getFireTimeAfter(nextFireTime);

            if(nextFireTime == null)
                break;

            //avoid infinite loop
            java.util.Calendar c = java.util.Calendar.getInstance();
            c.setTime(nextFireTime);
            if (c.get(java.util.Calendar.YEAR) > YEAR_TO_GIVEUP_SCHEDULING_AT) {
                nextFireTime = null;
            }

            if(nextFireTime != null && nextFireTime.before(now)) {
                long diff = now.getTime() - nextFireTime.getTime();
                if(diff >= misfireThreshold) {
                    nextFireTime = getFireTimeAfter(nextFireTime);
                }
            }
        }
    }

    @Override
    public void setNextFireTime(Date nextFireTime) {
        this.nextFireTime = nextFireTime;
    }

    @Override
    public void setPreviousFireTime(Date previousFireTime) {
        this.previousFireTime = previousFireTime;
    }

    @Override
    public ScheduleBuilder<WeekdaysTrigger> getScheduleBuilder() {
        WeekdaysScheduleBuilder sb = new WeekdaysScheduleBuilder(weekdays, triggerTime, initTime);

        switch(getMisfireInstruction()) {
            case MISFIRE_INSTRUCTION_FIRE_NOW : sb.withMisfireHandlingInstructionFireNow();
                break;
            case MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY: sb.withMisfireHandlingInstructionIgnoreMisfires();
                break;
        }

        return sb;
    }

    @Override
    public int getTimesTriggered() {
        return timesTriggered;
    }

    @Override
    public void setTimesTriggered(int timesTriggered) {
        this.timesTriggered = timesTriggered;
    }

    @Override
    public boolean hasAdditionalProperties() {
        return false;
    }

    @Override
    public Integer[] getWeekdays() {
        return weekdays;
    }

    @Override
    public String getTriggerTime() {
        return triggerTime;
    }

    @Override
    public Date getInitTime() {
        return this.startTime;
    }

    @Override
    public String getOtherParams() {
        return null;
    }
}

接着增加WeekdaysScheduleBuilder类:

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.quartz.ScheduleBuilder;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.spi.MutableTrigger;

import java.util.Date;

/**
 * @Author zhangyugu
 * @Date 2021/7/24 12:10 下午
 * @Version 1.0
 */
@Slf4j
public class WeekdaysScheduleBuilder extends ScheduleBuilder<WeekdaysTrigger> {

    private Integer[] weekdays;

    private String triggerTime;

    private Date startTime;

    private String otherParams;

    private int misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_SMART_POLICY;

    public WeekdaysScheduleBuilder(Integer[] weekdays, String triggerTime, Date startTime) {
        this.weekdays = weekdays;
        this.triggerTime = triggerTime;
        this.startTime = startTime;
    }

    public WeekdaysScheduleBuilder(Integer[] weekdays, String triggerTime, String otherParams, Date startTime) {
        this.weekdays = weekdays;
        this.triggerTime = triggerTime;
        this.startTime = startTime;
        this.otherParams = otherParams;
    }

    @Override
    public MutableTrigger build() {
        if(StringUtils.isBlank(otherParams)) {
            WeekdaysTriggerImpl weekdaysTrigger = new WeekdaysTriggerImpl(weekdays, triggerTime, startTime);
            weekdaysTrigger.setMisfireInstruction(misfireInstruction);
            return weekdaysTrigger;
        } else {
            SunSetRiseTriggerImpl sunSetRiseTrigger = new SunSetRiseTriggerImpl(weekdays, triggerTime, otherParams, startTime);
            sunSetRiseTrigger.setMisfireInstruction(misfireInstruction);
            return sunSetRiseTrigger;
        }
    }

    /**
     * If the Trigger misfires, use the
     * {@link Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY} instruction.
     *
     * @return the updated CronScheduleBuilder
     * @see Trigger#MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY
     */
    public WeekdaysScheduleBuilder withMisfireHandlingInstructionIgnoreMisfires() {
        misfireInstruction = Trigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY;
        return this;
    }

    /**
     * If the Trigger misfires, use the
     * {@link SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW} instruction.
     *
     * @return the updated SimpleScheduleBuilder
     * @see SimpleTrigger#MISFIRE_INSTRUCTION_FIRE_NOW
     */

    public WeekdaysScheduleBuilder withMisfireHandlingInstructionFireNow() {
        misfireInstruction = SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW;
        return this;
    }
}

最后是WeekdaysTrigger数据的实例化类WeekdaysTriggerPersistenceDelegate:

public class WeekdaysTriggerPersistenceDelegate implements TriggerPersistenceDelegate, StdJDBCConstants {

    protected String tablePrefix;
    protected String schedNameLiteral;

    public void initialize(String theTablePrefix, String schedName) {
        this.tablePrefix = theTablePrefix;
        this.schedNameLiteral = "'" + schedName + "'";
    }

    @Override
    public boolean canHandleTriggerType(OperableTrigger trigger) {
        return trigger instanceof WeekdaysTriggerImpl;
    }

    public String getHandledTriggerTypeDiscriminator() {
        return "WEEKDAYS";
    }

    @Override
    public int insertExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException {
        WeekdaysTrigger weekdaysTrigger = (WeekdaysTrigger) trigger;

        PreparedStatement ps = null;

        try {
            ps = conn.prepareStatement(Util.rtp(
                    "INSERT INTO "
                            + TABLE_PREFIX_SUBST + "WEEKDAYS_TRIGGERS" + " ("
                            + COL_SCHEDULER_NAME + ", "
                            + COL_TRIGGER_NAME + ", " + COL_TRIGGER_GROUP + ", "
                            + "REPEAT_DAYS" + ", "
                            + "TRIGGER_TIME, "
                            + "OTHER_PROPS, "
                            + "START_TIME, "
                            + COL_TIMES_TRIGGERED + ") " + " VALUES(" + SCHED_NAME_SUBST + ", ?, ?, ?, ?, ?, ?, ?)"
                    , tablePrefix, schedNameLiteral));
            ps.setString(1, trigger.getKey().getName());
            ps.setString(2, trigger.getKey().getGroup());
            ps.setString(3, IdSpliter.join(weekdaysTrigger.getWeekdays(), ","));
            ps.setString(4, weekdaysTrigger.getTriggerTime());
            ps.setString(5, weekdaysTrigger.getOtherParams());
            ps.setTimestamp(6, new Timestamp(weekdaysTrigger.getInitTime().getTime()));
            ps.setInt(7, weekdaysTrigger.getTimesTriggered());

            return ps.executeUpdate();
        } finally {
            Util.closeStatement(ps);
        }
    }

    @Override
    public int updateExtendedTriggerProperties(Connection conn, OperableTrigger trigger, String state, JobDetail jobDetail) throws SQLException, IOException {
        WeekdaysTrigger weekdaysTrigger = (WeekdaysTrigger) trigger;

        PreparedStatement ps = null;

        try {
            ps = conn.prepareStatement(Util.rtp("UPDATE "
                    + TABLE_PREFIX_SUBST + "WEEKDAYS_TRIGGERS" + " SET "
                    + "REPEAT_DAYS" + " = ?, "
                    + "TRIGGER_TIME" + " = ?, "
                    + "OTHER_PROPS" + " = ?, "
                    + "START_TIME" + " = ?, "
                    + COL_TIMES_TRIGGERED + " = ? WHERE "
                    + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST
                    + " AND " + COL_TRIGGER_NAME
                    + " = ? AND " + COL_TRIGGER_GROUP + " = ?",
                    tablePrefix, schedNameLiteral));

            ps.setString(1, IdSpliter.join(weekdaysTrigger.getWeekdays(), ","));
            ps.setString(2, weekdaysTrigger.getTriggerTime());
            ps.setString(3, weekdaysTrigger.getOtherParams());
            ps.setTimestamp(4, new Timestamp(weekdaysTrigger.getInitTime().getTime()));
            ps.setInt(5, weekdaysTrigger.getTimesTriggered());
            ps.setString(6, weekdaysTrigger.getKey().getName());
            ps.setString(7, weekdaysTrigger.getKey().getGroup());

            return ps.executeUpdate();
        } finally {
            Util.closeStatement(ps);
        }
    }

    @Override
    public int deleteExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException {
        PreparedStatement ps = null;

        try {
            ps = conn.prepareStatement(Util.rtp(
                    "DELETE FROM "
                            + TABLE_PREFIX_SUBST + "WEEKDAYS_TRIGGERS" + " WHERE "
                            + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST
                            + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?"
                    , tablePrefix, schedNameLiteral));
            ps.setString(1, triggerKey.getName());
            ps.setString(2, triggerKey.getGroup());

            return ps.executeUpdate();
        } finally {
            Util.closeStatement(ps);
        }
    }

    @Override
    public TriggerPropertyBundle loadExtendedTriggerProperties(Connection conn, TriggerKey triggerKey) throws SQLException {
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            ps = conn.prepareStatement(Util.rtp("SELECT *" + " FROM "
                    + TABLE_PREFIX_SUBST + "WEEKDAYS_TRIGGERS" + " WHERE "
                    + COL_SCHEDULER_NAME + " = " + SCHED_NAME_SUBST
                    + " AND " + COL_TRIGGER_NAME + " = ? AND " + COL_TRIGGER_GROUP + " = ?",
                    tablePrefix, schedNameLiteral));
            ps.setString(1, triggerKey.getName());
            ps.setString(2, triggerKey.getGroup());
            rs = ps.executeQuery();

            if (rs.next()) {
                String weekdays = rs.getString("REPEAT_DAYS");
                String triggerTime = rs.getString("TRIGGER_TIME");
                Timestamp startTime = rs.getTimestamp("START_TIME");
                String otherParams = rs.getString("OTHER_PROPS");
                int timesTriggered = rs.getInt(COL_TIMES_TRIGGERED);

                String[] statePropertyNames = { "timesTriggered" };
                Object[] statePropertyValues = { timesTriggered };

                return new TriggerPropertyBundle(new WeekdaysScheduleBuilder(IdSpliter.splitStr2IntArr(weekdays, ","), triggerTime, otherParams, startTime), statePropertyNames, statePropertyValues);
            }

            throw new IllegalStateException("No record found for selection of Trigger with key: '" + triggerKey + "' and statement: " + Util.rtp(SELECT_SIMPLE_TRIGGER, tablePrefix, schedNameLiteral));
        } finally {
            Util.closeResultSet(rs);
            Util.closeStatement(ps);
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Quartz是一个开源的作业调度框架,可以用来实现定时任务的调度和执行。它提供了很多的API和功能,其中一个重要的组件就是TriggerTrigger是用来触发一个Job的执行的组件,可以理解为定时器。当Trigger满足一定的条件时,就会触发对应的Job执行。 Quartz中的Trigger有很多种类型,比如SimpleTrigger、CronTrigger等等,每种Trigger都有自己的特点和用途。在Quartz中,我们可以监听Trigger的状态变化,比如Trigger被触发、Trigger被暂停、Trigger被删除等等。通过监听Trigger的状态变化,我们可以在某些特定的时刻执行一些操作,比如记录日志、发送通知等等。 要监听Trigger的状态变化,我们需要实现Quartz提供的TriggerListener接口,该接口定义了一些回调方法,比如triggerFired、triggerPaused、triggerResumed等等。当Trigger的状态发生变化时,Quartz会自动调用对应的回调方法,我们可以在回调方法中执行相应的操作。 下面是一个简单的Trigger监听器的示例代码: ```java public class MyTriggerListener implements TriggerListener { public String getName() { return "MyTriggerListener"; } public void triggerFired(Trigger trigger, JobExecutionContext context) { System.out.println("Trigger fired: " + trigger.getKey()); } public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) { return false; } public void triggerMisfired(Trigger trigger) { System.out.println("Trigger misfired: " + trigger.getKey()); } public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) { System.out.println("Trigger completed: " + trigger.getKey()); } } ``` 在上面的代码中,我们实现了TriggerListener接口,并重写了其中的几个回调方法。getName方法返回该监听器的名称,triggerFired方法在Trigger被触发时被调用,triggerMisfired方法在Trigger错过触发时被调用,triggerComplete方法在Trigger完成后被调用。在每个回调方法中,我们可以实现自己的逻辑。最后,我们需要将该监听器注册到Quartz的Scheduler中: ```java scheduler.getListenerManager().addTriggerListener(new MyTriggerListener()); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值