我接触的实际情况是excel文件导入多条商品功能,但是导入可能花费一定的时间,我们就想根据导入的数据做一个进度条,并且可以显示导入第几条,每导入一条,就显示相应的信息。
这里我们就用了作业调度,通过一个Job不断的运行,将我们的‘作业’放在job中,进行一个监控和访问。
我们使用quartz这一技术
那么我们需要一个作业调度器,然后set相应的作业,进行操作。
获取quartz的核心类
Scheduler scheduler = ApplicationContextUtils.getBean(DEFAULT_SCHEDULER_ID);
进行多任务的分组(相当于一个载体,用来存,是哪个作业,作业名称,返回的结果也在其中)
JobScheduleHandler handler = new JobScheduleHandler((String) params.get(ImpexConstants.PN_PLUGINNAME), jobCls.getSimpleName() + UUIDGenerator.getUUID());
// 为了支持并发多任务需要制定不同分组
我们new 一个作业
JobKey jobKey = new JobKey(handler.getName(), handler.getGroup());
如果作业调度已经执行过的作业,直接将调度去set到handle中 (context是作业任务的上下文),这里主要是有一些参数返回值,放到handler。
if (scheduler.checkExists(jobKey)) {
for (JobExecutionContext context : scheduler.getCurrentlyExecutingJobs()) {
if (jobKey.equals(context.getJobDetail().getKey())) {
handler.setScheduled(context.getFireTime());
break;
}
}
如果是新的任务,需要将参数设置到上下文。
(jobCls是具体实现的作业)
JobDetail jobDetail = JobBuilder.newJob(jobCls).withIdentity(jobKey).build();
for (Map.Entry<String, Object> entry : params.entrySet()) {
jobDetail.getJobDataMap().put(entry.getKey(), entry.getValue());
}
jobDetail.getJobDataMap().put(ImpexConstants.PN_OPERCTX, JsonUtil.objectToJson(operCtx));
Trigger trigger = TriggerBuilder.newTrigger().startNow().build(); //此处就是开始执行作业
scheduler.scheduleJob(jobDetail, trigger);
最后将执行后的handler返回,这里返回到界面,界面就可以收到改执行时间进行结构返回,包括第几条,等各种信息。
handler.setScheduled(StringUtil.toDate(
new SimpleDateFormat(JsonUtil.DATE_FORMAT).format(handler.getScheduled()),
StringUtil.DATE_FORMATS[1]));
return handler;
@RequestMapping(value = "importForStandard", method = RequestMethod.POST)
public @ResponseBody JobScheduleHandler importForStandard(@RequestBody ImpexParams params)
throws Hdpos4ServiceException {
Map<String, Object> paramsMap = new HashMap<String, Object>();
paramsMap.put(ImpexConstants.PN_LANG, Hdpos4Util.getSysLang());
paramsMap.put(ImpexConstants.PN_PLUGINNAME, params.getPluginName());
paramsMap.put(ImpexConstants.PN_MAXCOUNT, params.getMaxCount());
paramsMap.put(ImpexConstants.PN_FILEURLS, params.getFileUrls());
return JobScheduler.schedule(DiralcImportJob.class, paramsMap, getBeanOperateContext());
}
我们会Return也就是上面的handler
里面的DiralcImportJob,就是具体要执行的job(作业)
而作业当中,比如如何拿到参数等等需要一些基类,将context上下文设置进去。这些公共的方法就在基类中实现。
其中包含一些中断功能
public abstract class AbstractJob implements InterruptableJob {
private static final Logger logger = LoggerFactory.getLogger(AbstractJob.class);
private JobExecutionContext context;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobContext.initial(context);
this.context = context;
try {
preProcess();
doExecute();
postProcess(JobInstanceResult.completed, null);
} catch (InterruptedException e) {
postProcess(JobInstanceResult.interrupted, e);
} catch (Exception e) {
postProcess(JobInstanceResult.aborted, e);
} finally {
JobContext.free();
}
}
/**
* 执行作业。
*/
protected abstract void doExecute() throws InterruptedException, Exception;
/**
* 取得作业结果通知器对象。<br>
* 子类开发者可以通过重写此方法,实现发出作业执行结果通知的功能。
*
* @return 返回null将不会发出通知。
*/
protected JobNotifier getNotifier() {
return null;
}
@Override
public void interrupt() throws UnableToInterruptJobException {
logger.info("Try to interrupt the job.");
JobContext.interrupt(context);
}
/**
* 预处理。在{@link #doExecute()}之前被调用。
*/
protected void preProcess() {
assert context != null;
saveForStartingJob(context);
}
/**
* 后处理。在{@link #doExecute()}之后被调用。
*
* @param result
* 作业执行结果。禁止传入null。
* @param e
* 异常对象。
* @throws IllegalArgumentException
* 当参数result为null时抛出。
*/
protected void postProcess(JobInstanceResult result, Exception e) throws IllegalArgumentException {
assert context != null;
Assert.assertArgumentNotNull(result, "result");
log(result, e);
saveForFinishingJob(context, result);
notify(result, e);
}
private void log(JobInstanceResult result, Exception e) {
if (JobInstanceResult.completed.equals(result)) {
logger.info("Job Completed.");
} else if (JobInstanceResult.interrupted.equals(result)) {
logger.info("Job Interrupted.");
} else if (JobInstanceResult.aborted.equals(result)) {
logger.error("Job Aborted.", e);
} else {
assert false;
}
}
private void saveForStartingJob(JobExecutionContext context) {
assert context != null;
try {
PJobInstance jobInstance = new PJobInstanceConverter().convert(context);
PJobInstanceDao jobInstanceDao = DaoFactory.getInstance().getJobInstanceDao();
jobInstanceDao.save(jobInstance);
} catch (Exception e) {
logger.error("Fail to save for starting job.", e);
}
}
private void saveForFinishingJob(JobExecutionContext context, JobInstanceResult result) {
assert context != null;
assert result != null;
try {
PJobInstance jobInstance = new PJobInstanceConverter().convert(context);
jobInstance.setState(JobInstanceState.over);
jobInstance.setFinishedAt(new Date());
jobInstance.setResult(result);
PJobInstanceDao jobInstanceDao = DaoFactory.getInstance().getJobInstanceDao();
jobInstanceDao.save(jobInstance);
} catch (Exception e) {
logger.error(MessageFormat.format("Fail to save for finishing job: {0}.", result), e);
}
}
private void notify(JobInstanceResult result, Exception caught) {
assert result != null;
JobNotifier notifier = getNotifier();
if (notifier == null) {
return;
}
try {
notifier.nodify(result, caught, context);
} catch (Exception e) {
logger.error("Fail to notify.", e);
}
}
}