Java tutorial
/* * Copyright (C) 2017 Baifendian Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baifendian.swordfish.masterserver.quartz; import com.baifendian.swordfish.common.mail.EmailManager; import com.baifendian.swordfish.dao.FlowDao; import com.baifendian.swordfish.dao.enums.DepPolicyType; import com.baifendian.swordfish.dao.enums.ExecType; import com.baifendian.swordfish.dao.enums.FlowStatus; import com.baifendian.swordfish.dao.model.ExecutionFlow; import com.baifendian.swordfish.dao.model.ProjectFlow; import com.baifendian.swordfish.dao.model.Schedule; import com.baifendian.swordfish.dao.model.flow.DepWorkflow; import com.baifendian.swordfish.dao.utils.json.JsonUtil; import com.baifendian.swordfish.masterserver.master.ExecFlowInfo; import com.baifendian.swordfish.masterserver.utils.crontab.CrontabUtil; import java.text.MessageFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import org.apache.commons.collections.CollectionUtils; import org.quartz.CronExpression; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Workflow Job <p> */ public class FlowScheduleJob implements Job { /** * logger */ private final Logger logger = LoggerFactory.getLogger(FlowScheduleJob.class); /** * ?? */ private static final String NAME_SEPARATOR = "_"; /** * FlowScheduleJob ??? */ private static final String FLOW_SCHEDULE_JOB_NAME_PRIFIX = "Job_Flow"; /** * FlowScheduleJob ??? */ private static final String FLOW_SCHEDULE_JOB_GROUP_NAME_PRIFIX = "JobGroup_Flow"; /** * projectId */ public static final String PARAM_PROJECT_ID = "projectId"; /** * flowId */ public static final String PARAM_FLOW_ID = "flowId"; /** * schedule */ private static final String PARAM_SCHEDULE = "schedule"; /** * worker rpc client */ private static BlockingQueue<ExecFlowInfo> executionFlowQueue; /** * {@link FlowDao} */ private static FlowDao flowDao; /** * ? */ private static long checkInterval = 15 * 1000; /** * ? Job Job ?? <p> */ public static void init(BlockingQueue<ExecFlowInfo> executionFlowQueue, FlowDao flowDao) { FlowScheduleJob.executionFlowQueue = executionFlowQueue; FlowScheduleJob.flowDao = flowDao; } /** * */ @Override public void execute(JobExecutionContext context) throws JobExecutionException { // 1. ?? JobDataMap dataMap = context.getJobDetail().getJobDataMap(); int projectId = dataMap.getInt(PARAM_PROJECT_ID); int flowId = dataMap.getInt(PARAM_FLOW_ID); // ? Date scheduledFireTime = context.getScheduledFireTime(); // ? Date fireTime = context.getFireTime(); Date preFireTime = context.getPreviousFireTime(); logger.info("schedule trigger at:{}, trigger at:{}, flow id:{}", scheduledFireTime, fireTime, flowId); ProjectFlow flow = flowDao.projectFlowFindById(flowId); // workflow ? job if (flow == null) { deleteJob(projectId, flowId); logger.warn("workflow not existdelete scheduler task of projectId:{}, flowId:{}", projectId, flowId); return; } // ?? workflow ?? workflow ?? Schedule schedule = flowDao.querySchedule(flowId); if (schedule == null) { deleteJob(projectId, flowId); logger.warn( "workflow scheduler information not existdelete scheduler task of projectId:{}, flowId:{}", projectId, flowId); return; } // ? ExecutionFlow ExecutionFlow executionFlow; try { executionFlow = flowDao.scheduleFlowToExecution(projectId, flowId, flow.getOwnerId(), scheduledFireTime, ExecType.SCHEDULER, schedule.getFailurePolicy(), schedule.getMaxTryTimes(), null, null, schedule.getNotifyType(), schedule.getNotifyMails(), schedule.getTimeout()); executionFlow.setProjectId(projectId); executionFlow.setProjectName(flow.getProjectName()); executionFlow.setWorkflowName(flow.getName()); } catch (Exception e) { logger.error("insert execution flow error", e); throw new JobExecutionException(e); } // ?? List<DepWorkflow> deps = JsonUtil.parseObjectList(schedule.getDepWorkflowsStr(), DepWorkflow.class); boolean preCheck = true; // ?? if (CollectionUtils.isNotEmpty(deps) || schedule.getDepPolicy() == DepPolicyType.DEP_PRE) { logger.info("job: {} has dep need to check!", flowId); updateWaitingDepFlowStatus(executionFlow, FlowStatus.WAITING_DEP); } // ? if (preFireTime != null && schedule.getDepPolicy() == DepPolicyType.DEP_PRE) { logger.info("job: {} start check self dep ...", flowId); if (!checkSelfDep(flowId, schedule, fireTime, scheduledFireTime)) { logger.warn("job: {} check self dep no pass!", flowId); preCheck = false; updateWaitingDepFlowStatus(executionFlow, FlowStatus.DEP_FAILED); } logger.info("job: {} pass check self dep!", flowId); } // ?? if (preCheck && CollectionUtils.isNotEmpty(deps)) { logger.info("job: {} start check workflow dep, dep size: {} ...", flowId, deps.size()); if (!checkWorkflowsDep(deps, fireTime, scheduledFireTime, schedule.getTimeout())) { logger.warn("job: {} check workflow dep no pass!", flowId); preCheck = false; updateWaitingDepFlowStatus(executionFlow, FlowStatus.DEP_FAILED); } } if (preCheck) { sendToExecution(executionFlow); } else { EmailManager.sendMessageOfExecutionFlow(executionFlow); } } /** * ??? */ private boolean checkSelfDep(int flowId, Schedule schedule, Date fireTime, Date scheduleFireTime) { CronExpression cronExpression; try { cronExpression = CrontabUtil.parseCronExp(schedule.getCrontab()); } catch (Exception e) { String msg = MessageFormat.format("Self flow: {0} crontab: {1} parse error", flowId, schedule.getCrontab()); logger.error(msg, e); return false; } while (true) { ExecutionFlow executionFlow = getLastSelfExecFlow(flowId, cronExpression, scheduleFireTime); // ?? boolean isNotFinished = false; // ?? if (executionFlow != null) { FlowStatus flowStatus = executionFlow.getStatus(); if (flowStatus != null && flowStatus.typeIsSuccess()) { return true; } else if (flowStatus == null || flowStatus.typeIsNotFinished()) { isNotFinished = true; } } else { // ???? isNotFinished = true; } if (isNotFinished) { // ?, if (checkTimeout(fireTime.getTime(), schedule.getTimeout())) { logger.error("Wait for dependence workflow timeout"); return false; } try { Thread.sleep(checkInterval); } catch (InterruptedException e) { logger.error(e.getMessage(), e); return false; } } else { // ??, return false; } } } /** * ???? */ private boolean checkWorkflowsDep(List<DepWorkflow> deps, Date fireTime, Date scheduleFireTime, int timeout) { for (DepWorkflow depWorkflow : deps) { ProjectFlow depFlow = flowDao.projectFlowFindByPorjectNameAndName(depWorkflow.getProjectName(), depWorkflow.getWorkflowName()); // ??? if (depFlow == null) { continue; } Schedule depSchedule = flowDao.querySchedule(depFlow.getId()); CronExpression depCron; try { depCron = CrontabUtil.parseCronExp(depSchedule.getCrontab()); } catch (Exception e) { // ??? crontab logger.error("dep flow {} crontab parse error", depFlow.getId()); continue; } if (!checkWorkflowDep(depFlow.getId(), depCron, fireTime, scheduleFireTime, timeout)) { return false; } } return true; } /** * ????? */ private boolean checkWorkflowDep(int flowId, CronExpression cronExpression, Date fireTime, Date scheduleFireTime, int timeout) { while (true) { ExecutionFlow executionFlow = getLastExecFlow(flowId, cronExpression, scheduleFireTime); // ?? boolean isNotFinished = false; if (executionFlow != null) { // ???? FlowStatus flowStatus = executionFlow.getStatus(); if (flowStatus != null && flowStatus.typeIsSuccess()) { return true; } else if (flowStatus == null || flowStatus.typeIsNotFinished()) { isNotFinished = true; } } else { // ???? isNotFinished = true; } if (isNotFinished) { // ?, if (checkTimeout(fireTime.getTime(), timeout)) { logger.error("Wait for dependence workflow timeout"); return false; } try { Thread.sleep(checkInterval); } catch (InterruptedException e) { logger.error(e.getMessage(), e); return false; } } else { // ?, return false; } } } /** * ??? */ private ExecutionFlow getLastExecFlow(int flowId, CronExpression cronExpression, Date scheduleFireTime) { ExecutionFlow scheduleFlow = flowDao.executionFlowPreDate(flowId, scheduleFireTime); if (scheduleFlow != null) { Date nextDate = cronExpression.getTimeAfter(scheduleFlow.getScheduleTime()); if (nextDate == null || nextDate.getTime() <= scheduleFireTime.getTime()) { return null; } } else { return null; } ExecutionFlow startTimeFlow = flowDao.executionFlowByStartTimeAndScheduleTime(flowId, scheduleFlow.getScheduleTime(), scheduleFlow.getStartTime()); if (startTimeFlow != null) { Date nextDate = cronExpression.getTimeAfter(startTimeFlow.getScheduleTime()); if (nextDate != null && nextDate.getTime() > scheduleFireTime.getTime()) { return startTimeFlow; } } return scheduleFlow; } /** * ??? */ private ExecutionFlow getLastSelfExecFlow(int flowId, CronExpression cronExpression, Date scheduleFireTime) { ExecutionFlow scheduleFlow = flowDao.executionFlowPreDate2(flowId, scheduleFireTime); if (scheduleFlow != null) { Date nextDate = cronExpression.getTimeAfter(scheduleFlow.getScheduleTime()); if (nextDate == null || nextDate.getTime() < scheduleFireTime.getTime()) { return null; } } else { return null; } ExecutionFlow startTimeFlow = flowDao.executionFlowByStartTimeAndScheduleTime(flowId, scheduleFlow.getScheduleTime(), scheduleFlow.getStartTime()); if (startTimeFlow != null) { Date nextDate = cronExpression.getTimeAfter(startTimeFlow.getScheduleTime()); if (nextDate != null && nextDate.getTime() >= scheduleFireTime.getTime()) { return startTimeFlow; } } return scheduleFlow; } /** * workflow ? */ private void updateWaitingDepFlowStatus(ExecutionFlow executionFlow, FlowStatus flowStatus) { Date now = new Date(); executionFlow.setStatus(flowStatus); if (flowStatus != null && flowStatus.typeIsFinished()) { executionFlow.setEndTime(now); } flowDao.updateExecutionFlow(executionFlow); } /** * ? <p> * * @return ? */ private boolean checkTimeout(long startTime, Integer timeout) { if (timeout == null) { return false; } int usedTime = (int) ((System.currentTimeMillis() - startTime) / 1000); return timeout <= usedTime; } /** * job <p> */ private void deleteJob(int projectId, int flowId) { String jobName = genJobName(flowId); String jobGroupName = genJobGroupName(projectId); QuartzManager.deleteJob(jobName, jobGroupName); } /** * ?? worker <p> */ private void sendToExecution(ExecutionFlow executionFlow) { ExecFlowInfo execFlowInfo = new ExecFlowInfo(); execFlowInfo.setExecId(executionFlow.getId()); logger.info("scheduler to execution, exec id:{}, schedule time:{}", executionFlow.getId(), executionFlow.getScheduleTime()); executionFlowQueue.add(execFlowInfo); } /** * ? workflow ?? <p> */ public static String genJobName(int flowId) { StringBuilder builder = new StringBuilder(FLOW_SCHEDULE_JOB_NAME_PRIFIX); appendParam(builder, flowId); return builder.toString(); } /** * ? workflow ?? <p> */ public static String genJobGroupName(int projectId) { StringBuilder builder = new StringBuilder(FLOW_SCHEDULE_JOB_GROUP_NAME_PRIFIX); appendParam(builder, projectId); return builder.toString(); } /** * ??? <p> */ public static Map<String, Object> genDataMap(int projectId, int flowId, Schedule schedule) { Map<String, Object> dataMap = new HashMap<>(); dataMap.put(PARAM_PROJECT_ID, projectId); dataMap.put(PARAM_FLOW_ID, flowId); dataMap.put(PARAM_SCHEDULE, JsonUtil.toJsonString(schedule)); return dataMap; } /** * ? <p> */ private static void appendParam(StringBuilder builder, Object object) { builder.append(NAME_SEPARATOR); builder.append(object); } }