Yann Ann
2 years ago
committed by
GitHub
14 changed files with 788 additions and 475 deletions
@ -0,0 +1,88 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.service.command; |
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.dao.entity.Command; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessInstance; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessInstanceMap; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.TaskInstance; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Command Service |
||||||
|
*/ |
||||||
|
public interface CommandService { |
||||||
|
|
||||||
|
/** |
||||||
|
* Save error command, and delete original command. If the given command has already been moved into error command, |
||||||
|
* will throw {@link java.sql.SQLIntegrityConstraintViolationException ). |
||||||
|
* @param command command |
||||||
|
* @param message message |
||||||
|
*/ |
||||||
|
void moveToErrorCommand(Command command, String message); |
||||||
|
|
||||||
|
/** |
||||||
|
* Create new command |
||||||
|
* @param command command |
||||||
|
* @return result |
||||||
|
*/ |
||||||
|
int createCommand(Command command); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get command page |
||||||
|
* @param pageSize page size |
||||||
|
* @param pageNumber page number |
||||||
|
* @param masterCount master count |
||||||
|
* @param thisMasterSlot master slot |
||||||
|
* @return command page |
||||||
|
*/ |
||||||
|
List<Command> findCommandPageBySlot(int pageSize, int pageNumber, int masterCount, int thisMasterSlot); |
||||||
|
|
||||||
|
/** |
||||||
|
* check the input command exists in queue list |
||||||
|
* |
||||||
|
* @param command command |
||||||
|
* @return create command result |
||||||
|
*/ |
||||||
|
boolean verifyIsNeedCreateCommand(Command command); |
||||||
|
|
||||||
|
/** |
||||||
|
* create recovery waiting thread command when thread pool is not enough for the process instance. |
||||||
|
* sub work process instance need not create recovery command. |
||||||
|
* create recovery waiting thread command and delete origin command at the same time. |
||||||
|
* if the recovery command is exists, only update the field update_time |
||||||
|
* |
||||||
|
* @param originCommand originCommand |
||||||
|
* @param processInstance processInstance |
||||||
|
*/ |
||||||
|
void createRecoveryWaitingThreadCommand(Command originCommand, ProcessInstance processInstance); |
||||||
|
|
||||||
|
/** |
||||||
|
* create sub work process command |
||||||
|
* @param parentProcessInstance parent process instance |
||||||
|
* @param childInstance child process instance |
||||||
|
* @param instanceMap process instance map |
||||||
|
* @param task task instance |
||||||
|
* @return command |
||||||
|
*/ |
||||||
|
Command createSubProcessCommand(ProcessInstance parentProcessInstance, |
||||||
|
ProcessInstance childInstance, |
||||||
|
ProcessInstanceMap instanceMap, |
||||||
|
TaskInstance task); |
||||||
|
} |
@ -0,0 +1,272 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.service.command; |
||||||
|
|
||||||
|
import static org.apache.dolphinscheduler.common.Constants.CMD_PARAM_RECOVER_PROCESS_ID_STRING; |
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.common.Constants; |
||||||
|
import org.apache.dolphinscheduler.common.enums.CommandType; |
||||||
|
import org.apache.dolphinscheduler.common.enums.Flag; |
||||||
|
import org.apache.dolphinscheduler.common.enums.TaskDependType; |
||||||
|
import org.apache.dolphinscheduler.common.utils.JSONUtils; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.Command; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ErrorCommand; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessDefinition; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessInstance; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessInstanceMap; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.Schedule; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.TaskInstance; |
||||||
|
import org.apache.dolphinscheduler.dao.mapper.CommandMapper; |
||||||
|
import org.apache.dolphinscheduler.dao.mapper.ErrorCommandMapper; |
||||||
|
import org.apache.dolphinscheduler.dao.mapper.ProcessDefinitionMapper; |
||||||
|
import org.apache.dolphinscheduler.dao.mapper.ScheduleMapper; |
||||||
|
import org.apache.dolphinscheduler.plugin.task.api.enums.Direct; |
||||||
|
import org.apache.dolphinscheduler.plugin.task.api.model.Property; |
||||||
|
import org.apache.dolphinscheduler.service.utils.ParamUtils; |
||||||
|
|
||||||
|
import org.apache.commons.collections.CollectionUtils; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.EnumMap; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||||
|
import com.google.common.collect.Lists; |
||||||
|
import io.micrometer.core.annotation.Counted; |
||||||
|
|
||||||
|
/** |
||||||
|
* Command Service implementation |
||||||
|
*/ |
||||||
|
@Component |
||||||
|
public class CommandServiceImpl implements CommandService { |
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(CommandServiceImpl.class); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private ErrorCommandMapper errorCommandMapper; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private CommandMapper commandMapper; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private ScheduleMapper scheduleMapper; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private ProcessDefinitionMapper processDefineMapper; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void moveToErrorCommand(Command command, String message) { |
||||||
|
ErrorCommand errorCommand = new ErrorCommand(command, message); |
||||||
|
this.errorCommandMapper.insert(errorCommand); |
||||||
|
this.commandMapper.deleteById(command.getId()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@Counted("ds.workflow.create.command.count") |
||||||
|
public int createCommand(Command command) { |
||||||
|
int result = 0; |
||||||
|
if (command == null) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
// add command timezone
|
||||||
|
Schedule schedule = scheduleMapper.queryByProcessDefinitionCode(command.getProcessDefinitionCode()); |
||||||
|
if (schedule != null) { |
||||||
|
Map<String, String> commandParams = |
||||||
|
StringUtils.isNotBlank(command.getCommandParam()) ? JSONUtils.toMap(command.getCommandParam()) |
||||||
|
: new HashMap<>(); |
||||||
|
commandParams.put(Constants.SCHEDULE_TIMEZONE, schedule.getTimezoneId()); |
||||||
|
command.setCommandParam(JSONUtils.toJsonString(commandParams)); |
||||||
|
} |
||||||
|
command.setId(null); |
||||||
|
result = commandMapper.insert(command); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<Command> findCommandPageBySlot(int pageSize, int pageNumber, int masterCount, int thisMasterSlot) { |
||||||
|
if (masterCount <= 0) { |
||||||
|
return Lists.newArrayList(); |
||||||
|
} |
||||||
|
return commandMapper.queryCommandPageBySlot(pageSize, pageNumber * pageSize, masterCount, thisMasterSlot); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean verifyIsNeedCreateCommand(Command command) { |
||||||
|
boolean isNeedCreate = true; |
||||||
|
EnumMap<CommandType, Integer> cmdTypeMap = new EnumMap<>(CommandType.class); |
||||||
|
cmdTypeMap.put(CommandType.REPEAT_RUNNING, 1); |
||||||
|
cmdTypeMap.put(CommandType.RECOVER_SUSPENDED_PROCESS, 1); |
||||||
|
cmdTypeMap.put(CommandType.START_FAILURE_TASK_PROCESS, 1); |
||||||
|
CommandType commandType = command.getCommandType(); |
||||||
|
|
||||||
|
if (!cmdTypeMap.containsKey(commandType)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
ObjectNode cmdParamObj = JSONUtils.parseObject(command.getCommandParam()); |
||||||
|
int processInstanceId = cmdParamObj.path(CMD_PARAM_RECOVER_PROCESS_ID_STRING).asInt(); |
||||||
|
|
||||||
|
List<Command> commands = commandMapper.selectList(null); |
||||||
|
// for all commands
|
||||||
|
for (Command tmpCommand : commands) { |
||||||
|
if (!cmdTypeMap.containsKey(tmpCommand.getCommandType())) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
ObjectNode tempObj = JSONUtils.parseObject(tmpCommand.getCommandParam()); |
||||||
|
if (tempObj != null |
||||||
|
&& processInstanceId == tempObj.path(CMD_PARAM_RECOVER_PROCESS_ID_STRING).asInt()) { |
||||||
|
isNeedCreate = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return isNeedCreate; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void createRecoveryWaitingThreadCommand(Command originCommand, ProcessInstance processInstance) { |
||||||
|
// sub process doesn't need to create wait command
|
||||||
|
if (processInstance.getIsSubProcess() == Flag.YES) { |
||||||
|
if (originCommand != null) { |
||||||
|
commandMapper.deleteById(originCommand.getId()); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
Map<String, String> cmdParam = new HashMap<>(); |
||||||
|
cmdParam.put(Constants.CMD_PARAM_RECOVERY_WAITING_THREAD, String.valueOf(processInstance.getId())); |
||||||
|
// process instance quit by "waiting thread" state
|
||||||
|
if (originCommand == null) { |
||||||
|
Command command = new Command( |
||||||
|
CommandType.RECOVER_WAITING_THREAD, |
||||||
|
processInstance.getTaskDependType(), |
||||||
|
processInstance.getFailureStrategy(), |
||||||
|
processInstance.getExecutorId(), |
||||||
|
processInstance.getProcessDefinition().getCode(), |
||||||
|
JSONUtils.toJsonString(cmdParam), |
||||||
|
processInstance.getWarningType(), |
||||||
|
processInstance.getWarningGroupId(), |
||||||
|
processInstance.getScheduleTime(), |
||||||
|
processInstance.getWorkerGroup(), |
||||||
|
processInstance.getEnvironmentCode(), |
||||||
|
processInstance.getProcessInstancePriority(), |
||||||
|
processInstance.getDryRun(), |
||||||
|
processInstance.getId(), |
||||||
|
processInstance.getProcessDefinitionVersion(), |
||||||
|
processInstance.getTestFlag()); |
||||||
|
upsertCommand(command); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// update the command time if current command is recover from waiting
|
||||||
|
if (originCommand.getCommandType() == CommandType.RECOVER_WAITING_THREAD) { |
||||||
|
originCommand.setUpdateTime(new Date()); |
||||||
|
upsertCommand(originCommand); |
||||||
|
} else { |
||||||
|
// delete old command and create new waiting thread command
|
||||||
|
commandMapper.deleteById(originCommand.getId()); |
||||||
|
originCommand.setId(0); |
||||||
|
originCommand.setCommandType(CommandType.RECOVER_WAITING_THREAD); |
||||||
|
originCommand.setUpdateTime(new Date()); |
||||||
|
originCommand.setCommandParam(JSONUtils.toJsonString(cmdParam)); |
||||||
|
originCommand.setProcessInstancePriority(processInstance.getProcessInstancePriority()); |
||||||
|
upsertCommand(originCommand); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private int upsertCommand(@NotNull Command command) { |
||||||
|
if (command.getId() != null) { |
||||||
|
return commandMapper.updateById(command); |
||||||
|
} else { |
||||||
|
return commandMapper.insert(command); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Command createSubProcessCommand(ProcessInstance parentProcessInstance, ProcessInstance childInstance, |
||||||
|
ProcessInstanceMap instanceMap, TaskInstance task) { |
||||||
|
CommandType commandType = getSubCommandType(parentProcessInstance, childInstance); |
||||||
|
Map<String, Object> subProcessParam = JSONUtils.toMap(task.getTaskParams(), String.class, Object.class); |
||||||
|
long childDefineCode = 0L; |
||||||
|
if (subProcessParam.containsKey(Constants.CMD_PARAM_SUB_PROCESS_DEFINE_CODE)) { |
||||||
|
try { |
||||||
|
childDefineCode = |
||||||
|
Long.parseLong( |
||||||
|
String.valueOf(subProcessParam.get(Constants.CMD_PARAM_SUB_PROCESS_DEFINE_CODE))); |
||||||
|
} catch (NumberFormatException nfe) { |
||||||
|
logger.error("processDefinitionCode is not a number", nfe); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
ProcessDefinition subProcessDefinition = processDefineMapper.queryByCode(childDefineCode); |
||||||
|
|
||||||
|
Object localParams = subProcessParam.get(Constants.LOCAL_PARAMS); |
||||||
|
List<Property> allParam = JSONUtils.toList(JSONUtils.toJsonString(localParams), Property.class); |
||||||
|
Map<String, String> globalMap = ParamUtils.getGlobalParamMap(task.getVarPool()); |
||||||
|
Map<String, String> fatherParams = new HashMap<>(); |
||||||
|
if (CollectionUtils.isNotEmpty(allParam)) { |
||||||
|
for (Property info : allParam) { |
||||||
|
if (Direct.OUT == info.getDirect()) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
fatherParams.put(info.getProp(), globalMap.get(info.getProp())); |
||||||
|
} |
||||||
|
} |
||||||
|
String processParam = ParamUtils.getSubWorkFlowParam(instanceMap, parentProcessInstance, fatherParams); |
||||||
|
int subProcessInstanceId = |
||||||
|
childInstance == null ? 0 : (childInstance.getId() == null ? 0 : childInstance.getId()); |
||||||
|
return new Command( |
||||||
|
commandType, |
||||||
|
TaskDependType.TASK_POST, |
||||||
|
parentProcessInstance.getFailureStrategy(), |
||||||
|
parentProcessInstance.getExecutorId(), |
||||||
|
subProcessDefinition.getCode(), |
||||||
|
processParam, |
||||||
|
parentProcessInstance.getWarningType(), |
||||||
|
parentProcessInstance.getWarningGroupId(), |
||||||
|
parentProcessInstance.getScheduleTime(), |
||||||
|
task.getWorkerGroup(), |
||||||
|
task.getEnvironmentCode(), |
||||||
|
parentProcessInstance.getProcessInstancePriority(), |
||||||
|
parentProcessInstance.getDryRun(), |
||||||
|
subProcessInstanceId, |
||||||
|
subProcessDefinition.getVersion(), |
||||||
|
parentProcessInstance.getTestFlag()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* get sub work flow command type |
||||||
|
* child instance exist: child command = fatherCommand |
||||||
|
* child instance not exists: child command = fatherCommand[0] |
||||||
|
*/ |
||||||
|
private CommandType getSubCommandType(ProcessInstance parentProcessInstance, ProcessInstance childInstance) { |
||||||
|
CommandType commandType = parentProcessInstance.getCommandType(); |
||||||
|
if (childInstance == null) { |
||||||
|
String fatherHistoryCommand = parentProcessInstance.getHistoryCmd(); |
||||||
|
commandType = CommandType.valueOf(fatherHistoryCommand.split(Constants.COMMA)[0]); |
||||||
|
} |
||||||
|
return commandType; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.service.utils; |
||||||
|
|
||||||
|
import static org.apache.dolphinscheduler.common.Constants.*; |
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.common.utils.JSONUtils; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessInstance; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessInstanceMap; |
||||||
|
import org.apache.dolphinscheduler.plugin.task.api.model.Property; |
||||||
|
|
||||||
|
import org.apache.commons.collections.MapUtils; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
import com.google.common.base.Strings; |
||||||
|
|
||||||
|
/** |
||||||
|
* Param Utility class
|
||||||
|
*/ |
||||||
|
public class ParamUtils { |
||||||
|
|
||||||
|
/** |
||||||
|
* convert globalParams string to global parameter map |
||||||
|
* @param globalParams globalParams |
||||||
|
* @return parameter map |
||||||
|
*/ |
||||||
|
public static Map<String, String> getGlobalParamMap(String globalParams) { |
||||||
|
List<Property> propList; |
||||||
|
Map<String, String> globalParamMap = new HashMap<>(); |
||||||
|
if (!Strings.isNullOrEmpty(globalParams)) { |
||||||
|
propList = JSONUtils.toList(globalParams, Property.class); |
||||||
|
globalParamMap = propList.stream().collect(Collectors.toMap(Property::getProp, Property::getValue)); |
||||||
|
} |
||||||
|
return globalParamMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get sub workflow parameters |
||||||
|
* @param instanceMap process instance map |
||||||
|
* @param parentProcessInstance parent process instance |
||||||
|
* @param fatherParams fatherParams |
||||||
|
* @return sub workflow parameters |
||||||
|
*/ |
||||||
|
public static String getSubWorkFlowParam(ProcessInstanceMap instanceMap, ProcessInstance parentProcessInstance, |
||||||
|
Map<String, String> fatherParams) { |
||||||
|
// set sub work process command
|
||||||
|
String processMapStr = JSONUtils.toJsonString(instanceMap); |
||||||
|
Map<String, String> cmdParam = JSONUtils.toMap(processMapStr); |
||||||
|
if (parentProcessInstance.isComplementData()) { |
||||||
|
Map<String, String> parentParam = JSONUtils.toMap(parentProcessInstance.getCommandParam()); |
||||||
|
String endTime = parentParam.get(CMDPARAM_COMPLEMENT_DATA_END_DATE); |
||||||
|
String startTime = parentParam.get(CMDPARAM_COMPLEMENT_DATA_START_DATE); |
||||||
|
String scheduleTime = parentParam.get(CMDPARAM_COMPLEMENT_DATA_SCHEDULE_DATE_LIST); |
||||||
|
if (StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime)) { |
||||||
|
cmdParam.put(CMDPARAM_COMPLEMENT_DATA_END_DATE, endTime); |
||||||
|
cmdParam.put(CMDPARAM_COMPLEMENT_DATA_START_DATE, startTime); |
||||||
|
} |
||||||
|
if (StringUtils.isNotEmpty(scheduleTime)) { |
||||||
|
cmdParam.put(CMDPARAM_COMPLEMENT_DATA_SCHEDULE_DATE_LIST, scheduleTime); |
||||||
|
} |
||||||
|
processMapStr = JSONUtils.toJsonString(cmdParam); |
||||||
|
} |
||||||
|
if (MapUtils.isNotEmpty(fatherParams)) { |
||||||
|
cmdParam.put(CMD_PARAM_FATHER_PARAMS, JSONUtils.toJsonString(fatherParams)); |
||||||
|
processMapStr = JSONUtils.toJsonString(cmdParam); |
||||||
|
} |
||||||
|
return processMapStr; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,227 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.service.command; |
||||||
|
|
||||||
|
import static org.apache.dolphinscheduler.common.Constants.CMD_PARAM_RECOVER_PROCESS_ID_STRING; |
||||||
|
import static org.mockito.ArgumentMatchers.anyString; |
||||||
|
|
||||||
|
import org.apache.dolphinscheduler.common.Constants; |
||||||
|
import org.apache.dolphinscheduler.common.enums.CommandType; |
||||||
|
import org.apache.dolphinscheduler.common.enums.Flag; |
||||||
|
import org.apache.dolphinscheduler.common.enums.WarningType; |
||||||
|
import org.apache.dolphinscheduler.common.utils.DateUtils; |
||||||
|
import org.apache.dolphinscheduler.common.utils.JSONUtils; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.Command; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessDefinition; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessInstance; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.ProcessInstanceMap; |
||||||
|
import org.apache.dolphinscheduler.dao.entity.TaskInstance; |
||||||
|
import org.apache.dolphinscheduler.dao.mapper.CommandMapper; |
||||||
|
import org.apache.dolphinscheduler.dao.mapper.ProcessDefinitionMapper; |
||||||
|
import org.apache.dolphinscheduler.dao.mapper.ScheduleMapper; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||||
|
import org.mockito.InjectMocks; |
||||||
|
import org.mockito.Mock; |
||||||
|
import org.mockito.Mockito; |
||||||
|
import org.mockito.junit.jupiter.MockitoExtension; |
||||||
|
import org.mockito.junit.jupiter.MockitoSettings; |
||||||
|
import org.mockito.quality.Strictness; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class) |
||||||
|
@MockitoSettings(strictness = Strictness.LENIENT) |
||||||
|
class CommandServiceImplTest { |
||||||
|
|
||||||
|
@InjectMocks |
||||||
|
private CommandServiceImpl commandService; |
||||||
|
|
||||||
|
@Mock |
||||||
|
private CommandMapper commandMapper; |
||||||
|
|
||||||
|
@Mock |
||||||
|
private ProcessDefinitionMapper processDefineMapper; |
||||||
|
|
||||||
|
@Mock |
||||||
|
private ScheduleMapper scheduleMapper; |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testCreateSubCommand() { |
||||||
|
ProcessInstance parentInstance = new ProcessInstance(); |
||||||
|
parentInstance.setWarningType(WarningType.SUCCESS); |
||||||
|
parentInstance.setWarningGroupId(0); |
||||||
|
|
||||||
|
TaskInstance task = new TaskInstance(); |
||||||
|
task.setTaskParams("{\"processDefinitionCode\":10}}"); |
||||||
|
task.setId(10); |
||||||
|
task.setTaskCode(1L); |
||||||
|
task.setTaskDefinitionVersion(1); |
||||||
|
|
||||||
|
ProcessInstance childInstance = null; |
||||||
|
ProcessInstanceMap instanceMap = new ProcessInstanceMap(); |
||||||
|
instanceMap.setParentProcessInstanceId(1); |
||||||
|
instanceMap.setParentTaskInstanceId(10); |
||||||
|
Command command; |
||||||
|
|
||||||
|
// father history: start; child null == command type: start
|
||||||
|
parentInstance.setHistoryCmd("START_PROCESS"); |
||||||
|
parentInstance.setCommandType(CommandType.START_PROCESS); |
||||||
|
ProcessDefinition processDefinition = new ProcessDefinition(); |
||||||
|
processDefinition.setCode(10L); |
||||||
|
Mockito.when(processDefineMapper.queryByDefineId(100)).thenReturn(processDefinition); |
||||||
|
Mockito.when(processDefineMapper.queryByCode(10L)).thenReturn(processDefinition); |
||||||
|
command = commandService.createSubProcessCommand(parentInstance, childInstance, instanceMap, task); |
||||||
|
Assertions.assertEquals(CommandType.START_PROCESS, command.getCommandType()); |
||||||
|
|
||||||
|
// father history: start,start failure; child null == command type: start
|
||||||
|
parentInstance.setCommandType(CommandType.START_FAILURE_TASK_PROCESS); |
||||||
|
parentInstance.setHistoryCmd("START_PROCESS,START_FAILURE_TASK_PROCESS"); |
||||||
|
command = commandService.createSubProcessCommand(parentInstance, childInstance, instanceMap, task); |
||||||
|
Assertions.assertEquals(CommandType.START_PROCESS, command.getCommandType()); |
||||||
|
|
||||||
|
// father history: scheduler,start failure; child null == command type: scheduler
|
||||||
|
parentInstance.setCommandType(CommandType.START_FAILURE_TASK_PROCESS); |
||||||
|
parentInstance.setHistoryCmd("SCHEDULER,START_FAILURE_TASK_PROCESS"); |
||||||
|
command = commandService.createSubProcessCommand(parentInstance, childInstance, instanceMap, task); |
||||||
|
Assertions.assertEquals(CommandType.SCHEDULER, command.getCommandType()); |
||||||
|
|
||||||
|
// father history: complement,start failure; child null == command type: complement
|
||||||
|
|
||||||
|
String startString = "2020-01-01 00:00:00"; |
||||||
|
String endString = "2020-01-10 00:00:00"; |
||||||
|
parentInstance.setCommandType(CommandType.START_FAILURE_TASK_PROCESS); |
||||||
|
parentInstance.setHistoryCmd("COMPLEMENT_DATA,START_FAILURE_TASK_PROCESS"); |
||||||
|
Map<String, String> complementMap = new HashMap<>(); |
||||||
|
complementMap.put(Constants.CMDPARAM_COMPLEMENT_DATA_START_DATE, startString); |
||||||
|
complementMap.put(Constants.CMDPARAM_COMPLEMENT_DATA_END_DATE, endString); |
||||||
|
parentInstance.setCommandParam(JSONUtils.toJsonString(complementMap)); |
||||||
|
command = commandService.createSubProcessCommand(parentInstance, childInstance, instanceMap, task); |
||||||
|
Assertions.assertEquals(CommandType.COMPLEMENT_DATA, command.getCommandType()); |
||||||
|
|
||||||
|
JsonNode complementDate = JSONUtils.parseObject(command.getCommandParam()); |
||||||
|
Date start = DateUtils.stringToDate(complementDate.get(Constants.CMDPARAM_COMPLEMENT_DATA_START_DATE).asText()); |
||||||
|
Date end = DateUtils.stringToDate(complementDate.get(Constants.CMDPARAM_COMPLEMENT_DATA_END_DATE).asText()); |
||||||
|
Assertions.assertEquals(startString, DateUtils.dateToString(start)); |
||||||
|
Assertions.assertEquals(endString, DateUtils.dateToString(end)); |
||||||
|
|
||||||
|
// father history: start,failure,start failure; child not null == command type: start failure
|
||||||
|
childInstance = new ProcessInstance(); |
||||||
|
parentInstance.setCommandType(CommandType.START_FAILURE_TASK_PROCESS); |
||||||
|
parentInstance.setHistoryCmd("START_PROCESS,START_FAILURE_TASK_PROCESS"); |
||||||
|
command = commandService.createSubProcessCommand(parentInstance, childInstance, instanceMap, task); |
||||||
|
Assertions.assertEquals(CommandType.START_FAILURE_TASK_PROCESS, command.getCommandType()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testVerifyIsNeedCreateCommand() { |
||||||
|
|
||||||
|
List<Command> commands = new ArrayList<>(); |
||||||
|
|
||||||
|
Command command = new Command(); |
||||||
|
command.setCommandType(CommandType.REPEAT_RUNNING); |
||||||
|
command.setCommandParam("{\"" + CMD_PARAM_RECOVER_PROCESS_ID_STRING + "\":\"111\"}"); |
||||||
|
commands.add(command); |
||||||
|
Mockito.when(commandMapper.selectList(null)).thenReturn(commands); |
||||||
|
Assertions.assertFalse(commandService.verifyIsNeedCreateCommand(command)); |
||||||
|
|
||||||
|
Command command1 = new Command(); |
||||||
|
command1.setCommandType(CommandType.REPEAT_RUNNING); |
||||||
|
command1.setCommandParam("{\"" + CMD_PARAM_RECOVER_PROCESS_ID_STRING + "\":\"222\"}"); |
||||||
|
Assertions.assertTrue(commandService.verifyIsNeedCreateCommand(command1)); |
||||||
|
|
||||||
|
Command command2 = new Command(); |
||||||
|
command2.setCommandType(CommandType.PAUSE); |
||||||
|
Assertions.assertTrue(commandService.verifyIsNeedCreateCommand(command2)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testCreateRecoveryWaitingThreadCommand() { |
||||||
|
int id = 123; |
||||||
|
Mockito.when(commandMapper.deleteById(id)).thenReturn(1); |
||||||
|
ProcessInstance subProcessInstance = new ProcessInstance(); |
||||||
|
subProcessInstance.setIsSubProcess(Flag.YES); |
||||||
|
Command originCommand = new Command(); |
||||||
|
originCommand.setId(id); |
||||||
|
commandService.createRecoveryWaitingThreadCommand(originCommand, subProcessInstance); |
||||||
|
|
||||||
|
ProcessInstance processInstance = new ProcessInstance(); |
||||||
|
processInstance.setId(111); |
||||||
|
commandService.createRecoveryWaitingThreadCommand(null, subProcessInstance); |
||||||
|
|
||||||
|
Command recoverCommand = new Command(); |
||||||
|
recoverCommand.setCommandType(CommandType.RECOVER_WAITING_THREAD); |
||||||
|
commandService.createRecoveryWaitingThreadCommand(recoverCommand, subProcessInstance); |
||||||
|
|
||||||
|
Command repeatRunningCommand = new Command(); |
||||||
|
recoverCommand.setCommandType(CommandType.REPEAT_RUNNING); |
||||||
|
commandService.createRecoveryWaitingThreadCommand(repeatRunningCommand, subProcessInstance); |
||||||
|
|
||||||
|
ProcessInstance subProcessInstance2 = new ProcessInstance(); |
||||||
|
subProcessInstance2.setId(111); |
||||||
|
subProcessInstance2.setIsSubProcess(Flag.NO); |
||||||
|
commandService.createRecoveryWaitingThreadCommand(repeatRunningCommand, subProcessInstance2); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void giveNullOriginCommand_thenCreateRecoveryWaitingThreadCommand_expectNoDelete() { |
||||||
|
ProcessInstance subProcessInstance = new ProcessInstance(); |
||||||
|
subProcessInstance.setIsSubProcess(Flag.NO); |
||||||
|
subProcessInstance.setId(111); |
||||||
|
ProcessDefinition processDefinition = new ProcessDefinition(); |
||||||
|
processDefinition.setId(111); |
||||||
|
processDefinition.setCode(10L); |
||||||
|
subProcessInstance.setProcessDefinition(processDefinition); |
||||||
|
subProcessInstance.setWarningGroupId(1); |
||||||
|
commandService.createRecoveryWaitingThreadCommand(null, subProcessInstance); |
||||||
|
Mockito.verify(commandMapper, Mockito.times(0)).deleteById(anyString()); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testCreateCommand() { |
||||||
|
Command command = new Command(); |
||||||
|
command.setProcessDefinitionCode(123); |
||||||
|
command.setCommandParam("{\"ProcessInstanceId\":222}"); |
||||||
|
command.setCommandType(CommandType.START_PROCESS); |
||||||
|
int mockResult = 1; |
||||||
|
Mockito.when(commandMapper.insert(command)).thenReturn(mockResult); |
||||||
|
int exeMethodResult = commandService.createCommand(command); |
||||||
|
Assertions.assertEquals(mockResult, exeMethodResult); |
||||||
|
Mockito.verify(commandMapper, Mockito.times(1)).insert(command); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testFindCommandPageBySlot() { |
||||||
|
int pageSize = 1; |
||||||
|
int pageNumber = 0; |
||||||
|
int masterCount = 0; |
||||||
|
int thisMasterSlot = 2; |
||||||
|
List<Command> commandList = |
||||||
|
commandService.findCommandPageBySlot(pageSize, pageNumber, masterCount, thisMasterSlot); |
||||||
|
Assertions.assertEquals(0, commandList.size()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more |
||||||
|
* contributor license agreements. See the NOTICE file distributed with |
||||||
|
* this work for additional information regarding copyright ownership. |
||||||
|
* The ASF licenses this file to You 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 org.apache.dolphinscheduler.service.utils; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
public class ParamUtilsTest { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testGetGlobalParamMap() { |
||||||
|
String globalParam = "[{\"prop\":\"startParam1\",\"direct\":\"IN\",\"type\":\"VARCHAR\",\"value\":\"\"}]"; |
||||||
|
Map<String, String> globalParamMap = ParamUtils.getGlobalParamMap(globalParam); |
||||||
|
Assertions.assertEquals(globalParamMap.size(), 1); |
||||||
|
Assertions.assertEquals(globalParamMap.get("startParam1"), ""); |
||||||
|
|
||||||
|
Map<String, String> emptyParamMap = ParamUtils.getGlobalParamMap(null); |
||||||
|
Assertions.assertEquals(emptyParamMap.size(), 0); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue