Java tutorial
/** * Copyright 2016 benjobs * <p> * 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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * 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.opencron.server.service; import org.opencron.common.exception.PingException; import org.opencron.common.job.Action; import org.opencron.common.job.Request; import org.opencron.common.job.Response; import org.opencron.common.utils.ParamsMap; import org.opencron.server.domain.Record; import org.opencron.server.domain.Agent; import org.opencron.server.domain.User; import org.opencron.server.job.OpencronCaller; import org.opencron.server.job.OpencronMonitor; import org.opencron.server.vo.JobVo; import com.mysql.jdbc.PacketTooBigException; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; import static org.opencron.common.job.Opencron.*; @Service public class ExecuteService implements Job { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired private RecordService recordService; @Autowired private JobService jobService; @Autowired private NoticeService noticeService; @Autowired private OpencronCaller opencronCaller; @Autowired private AgentService agentService; @Autowired private UserService userService; private Map<Long, Integer> reExecuteThreadMap = new HashMap<Long, Integer>(0); private static final String PACKETTOOBIG_ERROR = "?MySQL????,?max_allowed_packet"; @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { String key = jobExecutionContext.getJobDetail().getKey().getName(); JobVo jobVo = (JobVo) jobExecutionContext.getJobDetail().getJobDataMap().get(key); try { ExecuteService executeService = (ExecuteService) jobExecutionContext.getJobDetail().getJobDataMap() .get("jobBean"); boolean success = executeService.executeJob(jobVo); this.loggerInfo("[opencron] job:{} at {}:{},execute:{}", jobVo, success ? "successful" : "failed"); } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); } } /** * ? */ public boolean executeJob(final JobVo job) { JobType jobType = JobType.getJobType(job.getJobType()); switch (jobType) { case SINGLETON: return executeSingleJob(job, job.getUserId());//? case FLOW: return executeFlowJob(job);//? default: return false; } } /** * ? */ private boolean executeSingleJob(JobVo job, Long userId) { if (!checkJobPermission(job.getAgentId(), userId)) return false; Record record = new Record(job); record.setJobType(JobType.SINGLETON.getCode());//? try { //?? record = recordService.save(record); //?? checkPing(job, record); Response response = responseToRecord(job, record); recordService.save(record); if (!response.isSuccess()) { //?????. if (job.getRedo() == 0 || job.getRunCount() == 0) { noticeService.notice(job, null); } this.loggerInfo("execute failed:jobName:{} at ip:{},port:{},info:{}", job, record.getMessage()); return false; } else { this.loggerInfo("execute successful:jobName:{} at ip:{},port:{}", job, null); } } catch (PacketTooBigException e) { noticeService.notice(job, PACKETTOOBIG_ERROR); this.loggerError("execute failed:jobName:%s at ip:%s,port:%d,info:%s", job, PACKETTOOBIG_ERROR, e); } catch (Exception e) { if (job.getRedo() == 0 || job.getRunCount() == 0) { noticeService.notice(job, null); } this.loggerError("execute failed:jobName:%s at ip:%s,port:%d,info:%s", job, e.getMessage(), e); } return record.getSuccess().equals(ResultStatus.SUCCESSFUL.getStatus()); } /** * ? ??? */ private boolean executeFlowJob(JobVo job) { if (!checkJobPermission(job.getAgentId(), job.getUserId())) return false; final long groupId = System.nanoTime() + Math.abs(new Random().nextInt());//??Id final Queue<JobVo> jobQueue = new LinkedBlockingQueue<JobVo>(); jobQueue.add(job); jobQueue.addAll(job.getChildren()); RunModel runModel = RunModel.getRunModel(job.getRunModel()); switch (runModel) { case SEQUENCE: return executeSequenceJob(groupId, jobQueue);// case SAMETIME: return executeSameTimeJob(groupId, jobQueue);// default: return false; } } /** * ?? */ private boolean executeSequenceJob(long groupId, Queue<JobVo> jobQueue) { for (JobVo jobVo : jobQueue) { if (!doFlowJob(jobVo, groupId)) { return false; } } return true; } /** * ?? */ private boolean executeSameTimeJob(final long groupId, final Queue<JobVo> jobQueue) { final List<Boolean> result = new ArrayList<Boolean>(0); Thread jobThread = new Thread(new Runnable() { @Override public void run() { for (final JobVo jobVo : jobQueue) { //?(?,??) Thread thread = new Thread(new Runnable() { public void run() { result.add(doFlowJob(jobVo, groupId)); } }); thread.start(); } } }); jobThread.start(); //?,?... try { jobThread.join(); } catch (InterruptedException e) { logger.error("[opencron] job rumModel with SAMETIME error:{}", e.getMessage()); } return !result.contains(false); } /** * ? */ private boolean doFlowJob(JobVo job, long groupId) { Record record = new Record(job); record.setGroupId(groupId);//Id record.setJobType(JobType.FLOW.getCode());//? record.setFlowNum(job.getFlowNum()); boolean success = true; try { //?? record = recordService.save(record); //?? checkPing(job, record); Response result = responseToRecord(job, record); if (!result.isSuccess()) { recordService.save(record); //kill, if (StatusCode.KILL.getValue().equals(result.getExitCode())) { recordService.flowJobDone(record); } else { success = false; } return false; } else { //???,? if (job.getLastChild()) { recordService.save(record); recordService.flowJobDone(record); } else { //?????,??... record.setStatus(RunStatus.RUNNING.getStatus()); recordService.save(record); } return true; } } catch (PingException e) { recordService.flowJobDone(record);//,?. return false; } catch (Exception e) { if (e instanceof PacketTooBigException) { record.setMessage(this.loggerError("execute failed(flow job):jobName:%s at ip:%s,port:%d,info:", job, PACKETTOOBIG_ERROR, e)); } else { record.setMessage(this.loggerError("execute failed(flow job):jobName:%s at ip:%s,port:%d,info:%s", job, e.getMessage(), e)); } record.setSuccess(ResultStatus.FAILED.getStatus());//? record.setReturnCode(StatusCode.ERROR_EXEC.getValue()); record.setEndTime(new Date()); recordService.save(record); success = false; return false; } finally { //???... if (!success) { Record red = recordService.get(record.getRecordId()); if (job.getRedo() == 1 && job.getRunCount() > 0) { int index = 0; boolean flag; do { flag = reExecuteJob(red, job, JobType.FLOW); ++index; } while (!flag && index < job.getRunCount()); //?,??,? if (!flag) { noticeService.notice(job, null); recordService.flowJobDone(record); } } else { noticeService.notice(job, null); recordService.flowJobDone(record); } } } } /** * ? */ public void batchExecuteJob(final Long userId, String command, String agentIds) { final Queue<JobVo> jobQueue = new LinkedBlockingQueue<JobVo>(); String[] arrayIds = agentIds.split(";"); for (String agentId : arrayIds) { Agent agent = agentService.getAgent(Long.parseLong(agentId)); JobVo jobVo = new JobVo(userId, command, agent); jobQueue.add(jobVo); } Thread jobThread = new Thread(new Runnable() { @Override public void run() { for (final JobVo jobVo : jobQueue) { //?(?,?) Thread thread = new Thread(new Runnable() { public void run() { executeSingleJob(jobVo, userId); } }); thread.start(); } } }); jobThread.start(); } /** * ? */ public boolean reExecuteJob(final Record parentRecord, JobVo job, JobType jobType) { if (parentRecord.getRedoCount().equals(reExecuteThreadMap.get(parentRecord.getRecordId()))) { return false; } else { reExecuteThreadMap.put(parentRecord.getRecordId(), parentRecord.getRedoCount()); } parentRecord.setStatus(RunStatus.RERUNNING.getStatus()); Record record = new Record(job); try { recordService.save(parentRecord); /** * ?? */ job.setExecType(ExecType.RERUN.getStatus()); record.setParentId(parentRecord.getRecordId()); record.setGroupId(parentRecord.getGroupId()); record.setJobType(jobType.getCode()); parentRecord.setRedoCount(parentRecord.getRedoCount() + 1);//? record.setRedoCount(parentRecord.getRedoCount()); record = recordService.save(record); //?? checkPing(job, record); Response result = responseToRecord(job, record); //???, if (result.isSuccess()) { parentRecord.setStatus(RunStatus.RERUNDONE.getStatus()); //???Kill,?? } else if (StatusCode.KILL.getValue().equals(result.getExitCode())) { parentRecord.setStatus(RunStatus.RERUNDONE.getStatus()); } else { //???,,?,?? if (parentRecord.getRunCount().equals(parentRecord.getRedoCount())) { noticeService.notice(job, null); } parentRecord.setStatus(RunStatus.RERUNUNDONE.getStatus()); } this.loggerInfo("execute successful:jobName:{} at ip:{},port:{}", job, null); } catch (Exception e) { if (e instanceof PacketTooBigException) { noticeService.notice(job, PACKETTOOBIG_ERROR); errorExec(record, this.loggerError("execute failed:jobName:%s at ip:%s,port:%d,info:%s", job, PACKETTOOBIG_ERROR, e)); } noticeService.notice(job, e.getMessage()); errorExec(record, this.loggerError("execute failed:jobName:%s at ip:%s,port:%d,info:%s", job, e.getMessage(), e)); } finally { //???? if (parentRecord.getRunCount().equals(parentRecord.getRedoCount())) { parentRecord.setStatus(RunStatus.RERUNDONE.getStatus()); } try { recordService.save(record); recordService.save(parentRecord); } catch (Exception e) { if (e instanceof PacketTooBigException) { record.setMessage(this.loggerError( "execute failed(flow job):jobName:%s at ip:%s,port:%d,info:" + PACKETTOOBIG_ERROR, job, e.getMessage(), e)); } else { record.setMessage( this.loggerError("execute failed(flow job):jobName:%s at ip:%s,port:%d,info:%s", job, e.getMessage(), e)); } } } return record.getSuccess().equals(ResultStatus.SUCCESSFUL.getStatus()); } /** * */ public boolean killJob(Record record) { final Queue<Record> recordQueue = new LinkedBlockingQueue<Record>(); //? if (JobType.SINGLETON.getCode().equals(record.getJobType())) { recordQueue.add(record); } else if (JobType.FLOW.getCode().equals(record.getJobType())) { //? recordQueue.addAll(recordService.getRunningFlowJob(record.getRecordId())); } final List<Boolean> result = new ArrayList<Boolean>(0); Thread jobThread = new Thread(new Runnable() { @Override public void run() { for (final Record cord : recordQueue) { //kill(?,?kill) Thread thread = new Thread(new Runnable() { public void run() { //??... cord.setStatus(RunStatus.STOPPING.getStatus());//? cord.setSuccess(ResultStatus.KILLED.getStatus());//?. JobVo job = null; try { recordService.save(cord); job = jobService.getJobVoById(cord.getJobId()); //???kill opencronCaller.call( Request.request(job.getIp(), job.getPort(), Action.KILL, job.getPassword()) .putParam("pid", cord.getPid()), job.getAgent()); cord.setStatus(RunStatus.STOPED.getStatus()); cord.setEndTime(new Date()); recordService.save(cord); loggerInfo("killed successful :jobName:{} at ip:{},port:{},pid:{}", job, cord.getPid()); } catch (Exception e) { if (e instanceof PacketTooBigException) { noticeService.notice(job, PACKETTOOBIG_ERROR); loggerError("killed error:jobName:%s at ip:%s,port:%d,pid:%s", job, cord.getPid() + " failed info: " + PACKETTOOBIG_ERROR, e); } noticeService.notice(job, null); loggerError("killed error:jobName:%s at ip:%s,port:%d,pid:%s", job, cord.getPid() + " failed info: " + e.getMessage(), e); result.add(false); } } }); thread.start(); } } }); jobThread.start(); //?kill,kill?... try { jobThread.join(); } catch (InterruptedException e) { logger.error("[opencron] kill job with error:{}", e.getMessage()); } return !result.contains(false); } /** * ????? */ private Response responseToRecord(final JobVo job, final Record record) throws Exception { Response response = opencronCaller .call(Request.request(job.getIp(), job.getPort(), Action.EXECUTE, job.getPassword()) .putParam("command", job.getCommand()).putParam("pid", record.getPid()) .putParam("timeout", job.getTimeout() + ""), job.getAgent()); logger.info("[opencron]:execute response:{}", response.toString()); record.setReturnCode(response.getExitCode()); record.setMessage(response.getMessage()); record.setSuccess( response.isSuccess() ? ResultStatus.SUCCESSFUL.getStatus() : ResultStatus.FAILED.getStatus()); if (StatusCode.KILL.getValue().equals(response.getExitCode())) { record.setStatus(RunStatus.STOPED.getStatus()); record.setSuccess(ResultStatus.KILLED.getStatus());//kill } else if (StatusCode.TIME_OUT.getValue().equals(response.getExitCode())) { record.setStatus(RunStatus.STOPED.getStatus()); record.setSuccess(ResultStatus.TIMEOUT.getStatus());//... } else { record.setStatus(RunStatus.DONE.getStatus()); } record.setStartTime(new Date(response.getStartTime())); record.setEndTime(new Date(response.getEndTime())); return response; } /** * ?? */ private void errorExec(Record record, String errorInfo) { record.setSuccess(ResultStatus.FAILED.getStatus());//? record.setStatus(RunStatus.DONE.getStatus());//? record.setReturnCode(StatusCode.ERROR_EXEC.getValue()); record.setEndTime(new Date()); record.setMessage(errorInfo); recordService.save(record); } /** * ? */ private void checkPing(JobVo job, Record record) throws PingException { if (!ping(job.getAgent())) { record.setStatus(RunStatus.DONE.getStatus());//? record.setReturnCode(StatusCode.ERROR_PING.getValue()); String format = "can't to communicate with agent:%s(%s:%d),execute job:%s failed"; String content = String.format(format, job.getAgentName(), job.getIp(), job.getPort(), job.getJobName()); record.setMessage(content); record.setSuccess(ResultStatus.FAILED.getStatus()); record.setEndTime(new Date()); recordService.save(record); throw new PingException(content); } } public boolean ping(Agent agent) { boolean ping = false; try { ping = opencronCaller .call(Request.request(agent.getIp(), agent.getPort(), Action.PING, agent.getPassword()) .putParam("serverPort", OpencronMonitor.port + ""), agent) .isSuccess(); } catch (Exception e) { logger.error("[opencron]ping failed,host:{},port:{}", agent.getIp(), agent.getPort()); } finally { return ping; } } /** * ? */ public boolean password(Agent agent, final String newPassword) { boolean ping = false; try { Response response = opencronCaller .call(Request.request(agent.getIp(), agent.getPort(), Action.PASSWORD, agent.getPassword()) .putParam("newPassword", newPassword), agent); ping = response.isSuccess(); } catch (Exception e) { e.printStackTrace(); } return ping; } /** * ?? */ public Response monitor(Agent agent) throws Exception { return opencronCaller.call( Request.request(agent.getIp(), agent.getPort(), Action.MONITOR, agent.getPassword()).setParams( ParamsMap.instance().fill("connType", ConnType.getByType(agent.getProxy()).getName())), agent); } /** * ?? */ private boolean checkJobPermission(Long jobAgentId, Long userId) { if (userId == null) return false; User user = userService.getUserById(userId); //??? if (user != null && user.getRoleId() == 999) return true; String agentIds = userService.getUserById(userId).getAgentIds(); agentIds = "," + agentIds + ","; String thisAgentId = "," + jobAgentId + ","; return agentIds.contains(thisAgentId); } private void loggerInfo(String str, JobVo job, String message) { if (message != null) { logger.info(str, job.getJobName(), job.getIp(), job.getPort(), message); } else { logger.info(str, job.getJobName(), job.getIp(), job.getPort()); } } private String loggerError(String str, JobVo job, String message, Exception e) { String errorInfo = String.format(str, job.getJobName(), job.getIp(), job.getPort(), message); logger.error(errorInfo, e); return errorInfo; } }