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();
|
}
|
|
}
|