定时任务插件兼容JFinal2.2

注: 该代码为玛雅妞代码改动而成, 只是JFinal2.2的logger改成了Log。 
        使用方法 : http://my.oschina.net/myaniu/blog/488386
package com.jfinal.test;

import com.jfinal.plugin.IPlugin;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.jfinal.kit.Prop;
import com.jfinal.kit.PropKit;
import com.jfinal.kit.StrKit;
import com.jfinal.log.Log;


import it.sauronsoftware.cron4j.Scheduler;
public class SchedulerPlugin implements IPlugin{
	private static Log LOG = Log.getLog("SchedulerPlugin");

	/**
	 * cron调度器
	 */
	private final Scheduler cronScheduler = new Scheduler();

	/**
	 * ScheduledThreadPoolExecutor调度器
	 */
	private final ScheduledThreadPoolExecutor taskScheduler;

	/**
	 * 调度任务配置文件
	 */
	private final String jobConfigFile;

	/**
	 * <p>
	 * Title: SchedulerPlugin
	 * </p>
	 * <p>
	 * Description: 构造函数(线程池依据系统核心数自动设定)
	 * </p>
	 * 
	 * @since V1.0.0
	 */
	public SchedulerPlugin() {
		this(getBestPoolSize(), null);
	}

	/**
	 * <p>
	 * Title: SchedulerPlugin
	 * </p>
	 * <p>
	 * Description: 构造函数(指定调度线程池大小)
	 * </p>
	 * 
	 * @param scheduledThreadPoolSize
	 *            调度线程池大小
	 * @since V1.3.0
	 */
	public SchedulerPlugin(int scheduledThreadPoolSize) {
		this(scheduledThreadPoolSize, null);
	}

	/**
	 * <p>
	 * Title: SchedulerPlugin
	 * </p>
	 * <p>
	 * Description: 构造函数(指定调度任务配置文件,线程池依据系统核心数自动设定)
	 * </p>
	 * 
	 * @param jobConfigFile
	 *            调度任务配置文件
	 * @since V1.0.0
	 */
	public SchedulerPlugin(String jobConfigFile) {
		this(getBestPoolSize(), jobConfigFile);
	}

	/**
	 * <p>
	 * Title: SchedulerPlugin
	 * </p>
	 * <p>
	 * Description: 构造函数(指定调度线程池大小和调度任务配置文件)
	 * </p>
	 * 
	 * @param scheduledThreadPoolSize
	 *            调度线程池大小
	 * @param jobConfigFile
	 *            调度任务配置文件
	 * @since V1.3.0
	 */
	public SchedulerPlugin(int scheduledThreadPoolSize, String jobConfigFile) {
		this.jobConfigFile = jobConfigFile;
		this.taskScheduler = new ScheduledThreadPoolExecutor(scheduledThreadPoolSize);
	}

	/**
	 * @Title: cronSchedule
	 * @Description: 添加基于Linux下的crontab表达式的调度任务(Runnable)
	 * @param task
	 *            定期执行的任务(Runnable)
	 * @param cronExpression
	 *            cron调度表达式
	 * @since V1.0.0
	 */
	public void cronSchedule(Runnable task, String cronExpression) {
		this.cronScheduler.schedule(cronExpression, task);
	}

	/**
	 * @Title: fixedRateSchedule
	 * @Description: 立即启动,并以固定的频率来运行任务。后续任务的启动时间不受前次任务延时影响(并行)。
	 * @param task
	 *            定期执行的任务
	 * @param periodSeconds
	 *            每次执行任务的间隔时间(单位秒)
	 * @return
	 * @since V1.0.0
	 */
	public ScheduledFuture<?> fixedRateSchedule(Runnable task, int periodSeconds) {
		return taskScheduler.scheduleAtFixedRate(task, 0, periodSeconds, TimeUnit.SECONDS);
	}

	/**
	 * @Title: fixedDelaySchedule
	 * @Description: 立即启动,两次任务间保持固定的时间间隔(任务串行执行,前一个结束之后间隔固定时间后一个才会启动)
	 * @param task
	 *            定期执行的任务
	 * @param periodSeconds
	 *            每次执行任务的间隔时间(单位秒)
	 * @return
	 * @since V1.0.0
	 */
	public ScheduledFuture<?> fixedDelaySchedule(Runnable task, int periodSeconds) {
		return taskScheduler.scheduleWithFixedDelay(task, 0, periodSeconds, TimeUnit.SECONDS);
	}

	@Override
	public boolean start() {
		if (this.jobConfigFile != null) {
			// 任务配置文件非空,从配置文件汇总加载任务
			loadJobsFromConfigFile();
		}
		this.cronScheduler.setDaemon(true);
		this.cronScheduler.start();
		LOG.info("SchedulerPlugin is started");
		return true;
	}

	@Override
	public boolean stop() {
		this.cronScheduler.stop();
		this.taskScheduler.shutdown();
		LOG.info("SchedulerPlugin is stopped");
		return true;
	}

	/**
	 * @Title: loadJobsFromConfigFile
	 * @Description: 从配置文件汇总加载任务
	 * @since V1.0.0
	 */
	private void loadJobsFromConfigFile() {
		// 获取job配置文件
		Prop jobProp = PropKit.use(this.jobConfigFile);
		// 获得所有任务名
		Set<String> jobNames = this.getJobNamesFromProp(jobProp);
		// 逐个加载任务
		for (String jobName : jobNames) {
			loadJob(jobProp, jobName);
		}
	}

	/**
	 * @Title: loadJob
	 * @Description: 加载一个任务
	 * @param jobProp
	 *            job配置
	 * @param jobName
	 *            job名
	 * @since V1.0.0
	 */
	private void loadJob(Prop jobProp, String jobName) {
		// 任务开关,默认开启
		Boolean enable = jobProp.getBoolean(jobName + ".enable", Boolean.TRUE);
		// 任务被禁用,直接返回
		if (!enable) {
			return;
		}
		// 创建要执行的任务
		Runnable task = createTask(jobName, jobProp.get(jobName + ".class"));
		// 任务类型
		String taskType = jobProp.get(jobName + ".type");
		if (StrKit.isBlank(taskType)) {
			throw new RuntimeException("Please set " + jobName + ".type");
		}
		// 任务表达式
		String expr = jobProp.get(jobName + ".expr");
		if (StrKit.isBlank(expr)) {
			throw new RuntimeException("Please set " + jobName + ".expr");
		}
		// 依据任务类型,开始调度任务
		scheduleJobByType(jobName, taskType, expr, task);
		LOG.info("--------load job: " + jobName + " successfully--------");
		LOG.info("class: " + jobProp.get(jobName + ".class"));
		LOG.info("type : " + taskType);
		LOG.info("expr : " + expr);
		LOG.info("----------------");
	}

	/**
	 * @Title: scheduleJobByType
	 * @Description: 依据任务类型,开始调度任务
	 * @param jobName
	 *            任务名
	 * @param taskType
	 *            任务类型
	 * @param expr
	 *            调度表达式
	 * @param task
	 *            执行的任务
	 * @since V1.0.0
	 */
	private void scheduleJobByType(String jobName, String taskType, String expr, Runnable task) {
		if ("cron".equals(taskType)) {
			this.cronSchedule(task, expr);
		} else if ("fixedRate".equals(taskType)) {
			int periodSeconds = 0;
			try {
				periodSeconds = Integer.parseInt(expr);
			} catch (NumberFormatException e) {
				throw new RuntimeException(jobName + ".expr must be a number");
			}
			this.fixedRateSchedule(task, periodSeconds);
		} else if ("fixedDelay".equals(taskType)) {
			int periodSeconds = 0;
			try {
				periodSeconds = Integer.parseInt(expr);
			} catch (NumberFormatException e) {
				throw new RuntimeException(jobName + ".expr must be a number");
			}
			this.fixedDelaySchedule(task, periodSeconds);
		} else {
			throw new RuntimeException("Please set " + jobName + ".type to cron/fixedRate/fixedDelay");
		}
	}

	/**
	 * @Title: createTask
	 * @Description: 创建任务
	 * @param jobName
	 *            任务名
	 * @param taskClassName
	 *            任务类名
	 * @return Runnable对象
	 * @since V1.0.0
	 */
	private Runnable createTask(String jobName, String taskClassName) {
		if (taskClassName == null) {
			throw new RuntimeException("Please set " + jobName + ".className");
		}

		Object temp = null;
		try {
			temp = Class.forName(taskClassName).newInstance();
		} catch (Exception e) {
			throw new RuntimeException("Can not create instance of class: " + taskClassName, e);
		}

		Runnable task = null;
		if (temp instanceof Runnable) {
			task = (Runnable) temp;
		} else {
			throw new RuntimeException("Can not create instance of class: " + taskClassName
					+ ". this class must implements Runnable interface");
		}
		return task;
	}

	/**
	 * @Title: getJobNamesFromProp
	 * @Description: 获得所有任务名
	 * @param jobProp
	 *            job配置
	 * @return 任务名集合
	 * @since V1.0.0
	 */
	private Set<String> getJobNamesFromProp(Prop jobProp) {
		Map<String, Boolean> jobNames = new HashMap<String, Boolean>();
		for (Object item : jobProp.getProperties().keySet()) {
			String fullKeyName = item.toString();
			// 获得job名
			String jobName = fullKeyName.substring(0, fullKeyName.indexOf("."));
			jobNames.put(jobName, Boolean.TRUE);
		}
		return jobNames.keySet();
	}

	/**
	 * @Title: getBestPoolSize
	 * @Description: 获得调度线程池大小
	 * @return
	 * @since V1.0.0
	 */
	private static int getBestPoolSize() {
		try {
			final int cores = Runtime.getRuntime().availableProcessors();
			// 每个核有8个调度线程
			return cores * 8;
		} catch (Throwable e) {
			return 8;
		}
	}
}


评论区

JFinal

2016-07-20 15:38

贴代码的时候可以利用编辑器上的第二个按钮进行添加,此时出来的格式会非常好看,就像这里的代码格式的样子:http://www.jfinal.com/project/1 ,感谢分享 ^_^

绿色的小苹果

2016-07-21 09:52

热门分享

扫码入社