package foundation.schedule; import java.util.Date; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.quartz.JobExecutionException; import org.quartz.JobKey; import org.quartz.TriggerKey; import foundation.action.ActionBucket; import foundation.action.ActionContext; import foundation.action.ActionMeta; import foundation.action.ActionProvider; import foundation.action.SingletonActionProvider; import foundation.dao.bizlogic.IActionProvider; import foundation.data.entity.Entity; import foundation.data.object.DataObject; import foundation.data.object.EntitySaver; import foundation.json.IJSONProvider; import foundation.json.IJSONWriter; import foundation.util.ContentBuilder; import foundation.util.ID; public class ScheduleJob implements IJSONProvider { private static final int Minutes_IntervalCycle = 1; private static final int Max_TryCycles = 2; private static final int Max_TryTimes = 3; protected static Logger logger; protected static ActionBucket actionBucket; private TriggerKey triggerKey; private JobKey jobKey; private int triggerTime; private String timeExpression; private Object lock; private ScheduleBatch batch; private JobMeta meta; private ActionMeta action; private JobState state; private int tryTimes; static { logger = LogManager.getLogger(ScheduleJob.class); actionBucket = ActionBucket.getInstance(); } public ScheduleJob() { triggerTime = 0; state = JobState.Idle; lock = new Object(); } public void load(Entity entity) { meta = new JobMeta(); meta.load(entity); triggerKey = new TriggerKey("trigger-" + meta.getId()); jobKey = new JobKey(meta.getId()); action = actionBucket.get(meta.getActionName()); } public void execute() throws JobExecutionException { triggerTime ++; try { String batchInstanceId = batch.getInstanceId(triggerTime); String jobInstanceId = ID.newValue(); //1. 如果上个 Job 还在运行,跳过 if (JobState.Working == state) { logger.info("{}.{} state is working, skip", batch.getTitle(), action.getName()); writeLogSkip(batchInstanceId, jobInstanceId); return; } //2. 执行 synchronized (lock) { if (JobState.Idle == state) { state = JobState.Working; tryTimes = 0; writeLogBegin(batchInstanceId, jobInstanceId); try { tryExecute(); writeLogEnd(jobInstanceId, ResultCode.Success); } catch (Exception e) { writeLogEnd(jobInstanceId, ResultCode.Fail); throw e; } finally { state = JobState.Idle; } } } } catch (Exception e) { logger.info("schedule error: {}", e.getMessage()); e.printStackTrace(); } } private void tryExecute() { int cnt = 0; boolean success = false; for (int j = 0; j < ScheduleJob.Max_TryCycles; j++) { for (int i = 0; i < ScheduleJob.Max_TryTimes; i++) { if (ScheduleCenter.Terminated) { break; } tryTimes++; try { cnt = cnt + 1; doExecute(); success = true; break; } catch (Exception e) { } } if (success) { break; } try { int waitfor = 60 * ScheduleJob.Minutes_IntervalCycle; for (int w = 0; w < waitfor; w++) { Thread.sleep(1000); if (ScheduleCenter.Terminated) { break; } } } catch (InterruptedException e) { } } } private void doExecute() throws Exception { IActionProvider provider = action.createProvider(); if (provider == null) { return; } ActionContext context = new ActionContext(); context.setProvider(provider); context.setParam(meta.getParam()); if (provider instanceof ActionProvider) { ActionProvider actionProvider = (ActionProvider)provider; actionProvider.setContext(context); } String method = action.getMethodName(); //1. 如果是测试模式,调用 Provider 的测试方法 if (ScheduleCenter.Test) { provider.test(context, method); return; } //2. 如果是正式模式,调用 Provider 的 exec 方法 if (provider instanceof ActionProvider) { ActionProvider actionProvider = (ActionProvider)provider; actionProvider.setContext(context); provider.exec(context, method); } else if (provider instanceof SingletonActionProvider) { SingletonActionProvider actionProvider = (SingletonActionProvider)provider; actionProvider.exec(context, method); } } private void writeLogBegin(String batchInstanceId, String instanceId) throws Exception { DataObject dataObject = DataObject.getInstance("sys_schedule_log_detail"); EntitySaver saver = dataObject.createEntitySaver(); saver.set("id", instanceId); saver.set("batch_id", batch.getId()); saver.set("batch_instance_id", batchInstanceId); saver.set("action_name", action.getName()); saver.set("action_title", meta.getRemark()); saver.set("begin_time", new Date()); saver.insert(); } private void writeLogEnd(String instanceId, ResultCode result) throws Exception { DataObject dataObject = DataObject.getInstance("sys_schedule_log_detail"); EntitySaver saver = dataObject.createEntitySaver(instanceId); saver.set("end_time", new Date()); saver.set("result_code", result.name()); saver.set("try_times", tryTimes); saver.update(); } private void writeLogSkip(String batchInstanceId, String instanceId) throws Exception { DataObject dataObject = DataObject.getInstance("sys_schedule_log_detail"); EntitySaver saver = dataObject.createEntitySaver(); Date now = new Date(); saver.set("id", instanceId); saver.set("batch_id", batch.getId()); saver.set("batch_instance_id", batchInstanceId); saver.set("action_name", action.getName()); saver.set("action_title", meta.getRemark()); saver.set("begin_time", now); saver.set("end_time", now); saver.set("result_code", ResultCode.Skip); saver.insert(); } public int setTimeExpression(int timeExpressionMinute, String timeExpression) { ContentBuilder result = new ContentBuilder(" "); String[] segments = timeExpression.split(" "); for (int i = 0; i < segments.length; i++){ String segment = segments[i]; if (i == 1){ timeExpressionMinute ++; if (segment.contains("/")) { String cycleMinute = segment.substring(segment.indexOf("/")); segment = timeExpressionMinute + cycleMinute; } else { segment = String.valueOf(timeExpressionMinute); } } result.append(segment); } this.timeExpression = result.toString(); return timeExpressionMinute; } public boolean isActive() { return meta.isAcitve(); } public String getRunTime() { return timeExpression; } public TriggerKey getTriggerKey() { return triggerKey; } public JobKey getJobKey() { return jobKey; } public String getBatchId() { return meta.getBatchId(); } public String getId() { return meta.getId(); } public void setBatch(ScheduleBatch scheduleBatch) { batch = scheduleBatch; } @Override public void writeJSON(IJSONWriter writer) { writer.beginObject(); writer.write("time_expression", timeExpression); writer.write("trigger_key", triggerKey.getName()); writer.write("job_key", jobKey.getName()); meta.writeJSONBody(writer); writer.endObject(); } }