com.baifendian.swordfish.masterserver.quartz.FlowScheduleJob.java Source code

Java tutorial

Introduction

Here is the source code for com.baifendian.swordfish.masterserver.quartz.FlowScheduleJob.java

Source

/*
 * 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);
    }
}