基于quartz触发器管理及动态创建触发器

年前做了个触发器管理,将以前设计不合理的地方修改掉了。

所谓动态创建,就是触发器的时间、触发器的任务由用户自己去设定,当然这个任务还是需要开发人员手动去写。

以前同事做的触发器管理,将某个任务写死,没法做到扩展,也没有持久化方面的考虑

后来参考 http://sundoctor.iteye.com/blog/399980  中提到的持久化方法,进行修改,已经满足要求。


本文使用的是quartz的1.7版本,quartz好像是分为两部分,一部分是任务(job)一个是触发器(trigger);trigger运行过程中,达到触发时间条件,将会执行job。

触发器管理模块需要提供触发器动态创建、删除、暂停、恢复功能,并结合spring做到job的扩展

在与spring整合时,有两个配置项 jobDetails和triggers

<bean id="schedulerFactory" lazy-init='false'
	class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		
	<property name="dataSource" ref="dataSource" />
	<property name="applicationContextSchedulerContextKey"
		value="applicationContextKey" />
	<property name="configLocation"
		value="classpath:quartz.properties" />
	<property name="startupDelay" value="30" />
		
	<!-- property name="overwriteExistingJobs" value="true" / -->
	<!-- 可用的job列表 -->
	<property name="jobDetails">
		<list>
			<!-- 示例 -->
			<ref bean="avicinfo.test.testJobService" />
		</list>
	</property>

	<!-- 默认启动的trigger列表 -->
	<property name="triggers">
		<list>
			<ref bean="OnlineDocDeleteRubbishFilesJobTrigger" />
		</list>
	</property>
</bean>

triggers中配置的信息已经是完整的触发器了,包含时间、job等信息,这部分会在SchedulerFactoryBean生成Scheduler对象时,就已经加载到触发器列表中了。

jobDetails是job的配置部分,想要做到由用户选择触发什么job,就要从这里入手。

通过quartz的api了解到,我使用的这个版本,没有直接提供获取所有job的方法,而是可以通过查找所有jobGroupName,得到最后的job对象,而在我的触发器管理模块中,默认启动的trigger是不能被删除的,所以将这些trigger中job的group不做设置,即默认组(DEFAULT),而其他在jobDetails中配置的job需要强制设定组信息,并且还要实现固定的接口,以达到对这些动态创建trigger的管理。

获取可以用于动态创建的job

	public List<JobDetail> getCanUseQrtzJobs() throws SchedulerException {
		List<JobDetail> list = new ArrayList<JobDetail>();
		String[] jobGroupNames = this.scheduler.getJobGroupNames();
		for (String jobGroupName : jobGroupNames) {
			// 去掉受保护的分组
			if (!CANNOT_USE_GROUPS.contains(jobGroupName)) {
				String[] jobNames = this.scheduler.getJobNames(jobGroupName);
				for (String jobName : jobNames) {
					JobDetail jd = this.scheduler.getJobDetail(jobName, jobGroupName);
					// 获取实际调度对象
					Object targetObject = jd.getJobDataMap().get("targetObject");
					if (StaticMethod.hasInterface(targetObject.getClass(), CAN_USE_INTERFACES)) {
						try {
							Object obj = PropertyUtils.getProperty(targetObject, "triggerName");
							if (StaticMethod.isEmpty(obj)) {
								list.add(jd);
							}
						} catch (IllegalAccessException e) {
							e.printStackTrace();
						} catch (InvocationTargetException e) {
							e.printStackTrace();
						} catch (NoSuchMethodException e) {
							e.printStackTrace();
						}

					}
				}
			}
		}
		return list;
	}
job需要实现的接口,提供管理连接,和唯一标识这个trigger的属性,我使用的是triggerName,全局唯一。

/**
 * @author lisen
 * @date Jan 17, 2014 1:56:42 PM
 */
public interface SchedulerJobDetail extends Cloneable {

	/**
	 * 获得展现页面的地址 不包括ip 端口号 工程名
	 * 
	 * @return
	 */
	String getActionUrl();

	/**
	 * 显示名称
	 * 
	 * @return
	 */
	String getDisplayName();

	/**
	 * 设置触发器名 对象唯一识别
	 * 
	 * @param triggerName
	 */
	void setTriggerName(String triggerName);

	/**
	 * 获取触发器名
	 * 
	 * @return
	 */
	String getTriggerName();

}
在启动一个trigger时,可以根据选择的jobName,jobGroup,获取到这个job的配置对象,将它塞到创建的trigger对象中,但是在这个trigger执行完毕后,他的job也随之被释放了,持久化的对象被删除。而quartz提供的持久化方法,仅仅判断jobName和jobGroup,所以在创建时,要在获取配置中得到job的name做下手脚,让他变成一个唯一对象,这样,trigger的生命周期如何,都和我们配置job没有关系了

    @Override
    public void schedule(String triggerName, String triggerGroup, String jobDetailName,
            String jobDetailGroup, String cronExpression, boolean overwriteExistingTrigger) {
        try {
            // 保证有这个job
            JobDetail tjd = this.scheduler.getJobDetail(jobDetailName, jobDetailGroup);
            if (!StaticMethod.isEmpty(tjd)) {
                JobDetail jd = (JobDetail) tjd.clone();
                // 处理获取到的job
                handleJobDetail(jd, triggerName);
                this.scheduler.addJob(jd, true);
                Trigger t = this.scheduler.getTrigger(triggerName, triggerGroup);
                if (!StaticMethod.isEmpty(t) && overwriteExistingTrigger) {
                    this.removeTrigger(triggerName, triggerGroup);
                }
                CronTrigger cronTrigger = new CronTrigger(triggerName, triggerGroup, jd.getName(),
                        jd.getGroup());
                cronTrigger.setCronExpression(cronExpression);
                this.scheduler.scheduleJob(cronTrigger);
            }
        } catch (Exception e) {
            throw new SchedulerRuntimeException(e);
        }
    }

    protected void handleJobDetail(JobDetail jd, String triggerName) {
        jd.setDescription(jd.getName());
        jd.setName(UUID.randomUUID().toString());
        // 获取实际调度对象
        Object targetObject = jd.getJobDataMap().get("targetObject");
        if (StaticMethod.hasInterface(targetObject.getClass(), CAN_USE_INTERFACES)) {
            try {
                Object cloneTargetObject = MethodUtils.invokeMethod(targetObject, "clone", null);
                PropertyUtils.setProperty(cloneTargetObject, "triggerName", triggerName);
                jd.getJobDataMap().put("targetObject", cloneTargetObject);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }

具体到job的配置

<bean id="avicinfo.test.testJobService"
	class="frameworkx.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	<property name="targetObject"
		ref="avicinfo.test.testManageService">
	</property>
	<property name="group" value="test"></property>
	<property name="targetMethod">
		<value>testMethod</value>
	</property>
	<property name="concurrent" value="false" />
	<property name="shouldRecover" value="true" />
</bean>
<bean id="avicinfo.test.testManageService"
    class="com.avicinfo.v2.core.quartz.test.service.impl.MyJobService">
    <property name="jdbcTemplate" value="jdbcTemplate"></property>
</bean>
job的实现

/**
 * @author lisen
 * @date Jan 17, 2014 2:07:18 PM
 */
public class MyJobService implements Serializable, SchedulerJobDetail, IMyJobService {
    /**
     * 
     */
    private static final long serialVersionUID = 5735518271578346479L;

    private String jdbcTemplate;

    private String triggerName;

    /*
     * (non-Javadoc)
     * 
     * @see com.avicinfo.v2.core.quartz.test.service.bo.IMyJobService#testMethod()
     */
    @Override
    public void testMethod() {
        String sql = "select sysdate from dual";
        Object result = this.getJdbcTemplateObj().queryForObject(sql, Date.class);
        System.out.println(result + "  测试job: " + this.getTriggerName() + " "
                + this.getClass().getName());
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.avicinfo.v2.core.quartz.service.bo.SchedulerJobDetail#getActionUrl()
     */
    @Override
    public String getActionUrl() {
        return "quartz/test/testAction.dd";
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.avicinfo.v2.core.quartz.service.bo.SchedulerJobDetail#getDisplayName()
     */
    @Override
    public String getDisplayName() {
        return "测试job";
    }

    /**
     * @return the jdbcTemplate
     */
    public String getJdbcTemplate() {
        return jdbcTemplate;
    }

    /**
     * @param jdbcTemplate
     *            the jdbcTemplate to set
     */
    public void setJdbcTemplate(String jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public JdbcTemplate getJdbcTemplateObj() {
        return SpringBean.getBean(JdbcTemplate.class, getJdbcTemplate());
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.avicinfo.v2.core.quartz.test.service.bo.IMyJobService#saveConfig(java.util.Map)
     */
    @Override
    public void saveConfig(Map map) {
        System.out.println("config:" + map);
        // this.jdbcTemplate.saveConfig .....
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.avicinfo.v2.core.quartz.service.bo.SchedulerJobDetail#setTriggerName(java.lang.String)
     */
    @Override
    public void setTriggerName(String triggerName) {
        this.triggerName = triggerName;
    }

    /**
     * @return the triggerName
     */
    public String getTriggerName() {
        return triggerName;
    }

    public Object clone() {
        MyJobService copy;
        try {
            copy = (MyJobService) super.clone();
        } catch (CloneNotSupportedException ex) {
            throw new IncompatibleClassChangeError("Not Cloneable.");
        }
        return copy;
    }
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值