4 files modified
46 files added
| | |
| | | <docker.password>Harbor12345</docker.password> |
| | | <docker.namespace>blade</docker.namespace> |
| | | <docker.plugin.version>1.4.13</docker.plugin.version> |
| | | |
| | | <netty-all.version>4.1.63.Final</netty-all.version> |
| | | <groovy.version>3.0.8</groovy.version> |
| | | </properties> |
| | | |
| | | <dependencyManagement> |
| | |
| | | <artifactId>weixin-java-pay</artifactId> |
| | | <version>4.4.9.B</version> |
| | | </dependency> |
| | | <!--xxl-job-core 相关依赖--> |
| | | <dependency> |
| | | <groupId>io.netty</groupId> |
| | | <artifactId>netty-all</artifactId> |
| | | <version>${netty-all.version}</version> |
| | | </dependency> |
| | | <!-- groovy-all --> |
| | | <dependency> |
| | | <groupId>org.codehaus.groovy</groupId> |
| | | <artifactId>groovy</artifactId> |
| | | <version>${groovy.version}</version> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | <build> |
| New file |
| | |
| | | package com.xxl.job.core.biz; |
| | | |
| | | import com.xxl.job.core.biz.model.HandleCallbackParam; |
| | | import com.xxl.job.core.biz.model.RegistryParam; |
| | | import com.xxl.job.core.biz.model.ReturnT; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public interface AdminBiz { |
| | | |
| | | // ---------------------- callback ---------------------- |
| | | |
| | | /** |
| | | * callback |
| | | * |
| | | * @param callbackParamList |
| | | * @return |
| | | */ |
| | | public ReturnT<String> callback(List<HandleCallbackParam> callbackParamList); |
| | | |
| | | // ---------------------- registry ---------------------- |
| | | |
| | | /** |
| | | * registry |
| | | * |
| | | * @param registryParam |
| | | * @return |
| | | */ |
| | | public ReturnT<String> registry(RegistryParam registryParam); |
| | | |
| | | /** |
| | | * registry remove |
| | | * |
| | | * @param registryParam |
| | | * @return |
| | | */ |
| | | public ReturnT<String> registryRemove(RegistryParam registryParam); |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz; |
| | | |
| | | import com.xxl.job.core.biz.model.*; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public interface ExecutorBiz { |
| | | |
| | | /** |
| | | * beat |
| | | * |
| | | * @return |
| | | */ |
| | | public ReturnT<String> beat(); |
| | | |
| | | /** |
| | | * idle beat |
| | | * |
| | | * @param idleBeatParam |
| | | * @return |
| | | */ |
| | | public ReturnT<String> idleBeat(IdleBeatParam idleBeatParam); |
| | | |
| | | /** |
| | | * run |
| | | * |
| | | * @param triggerParam |
| | | * @return |
| | | */ |
| | | public ReturnT<String> run(TriggerParam triggerParam); |
| | | |
| | | /** |
| | | * kill |
| | | * |
| | | * @param killParam |
| | | * @return |
| | | */ |
| | | public ReturnT<String> kill(KillParam killParam); |
| | | |
| | | /** |
| | | * log |
| | | * |
| | | * @param logParam |
| | | * @return |
| | | */ |
| | | public ReturnT<LogResult> log(LogParam logParam); |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.client; |
| | | |
| | | import com.xxl.job.core.biz.AdminBiz; |
| | | import com.xxl.job.core.biz.model.HandleCallbackParam; |
| | | import com.xxl.job.core.biz.model.RegistryParam; |
| | | import com.xxl.job.core.biz.model.ReturnT; |
| | | import com.xxl.job.core.util.XxlJobRemotingUtil; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * admin api test |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class AdminBizClient implements AdminBiz { |
| | | |
| | | public AdminBizClient() { |
| | | } |
| | | |
| | | public AdminBizClient(String addressUrl, String accessToken) { |
| | | this.addressUrl = addressUrl; |
| | | this.accessToken = accessToken; |
| | | |
| | | // valid |
| | | if (!this.addressUrl.endsWith("/")) { |
| | | this.addressUrl = this.addressUrl + "/"; |
| | | } |
| | | } |
| | | |
| | | private String addressUrl; |
| | | private String accessToken; |
| | | private int timeout = 3; |
| | | |
| | | |
| | | @Override |
| | | public ReturnT<String> callback(List<HandleCallbackParam> callbackParamList) { |
| | | return XxlJobRemotingUtil.postBody(addressUrl + "api/callback", accessToken, timeout, callbackParamList, String.class); |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<String> registry(RegistryParam registryParam) { |
| | | return XxlJobRemotingUtil.postBody(addressUrl + "api/registry", accessToken, timeout, registryParam, String.class); |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<String> registryRemove(RegistryParam registryParam) { |
| | | return XxlJobRemotingUtil.postBody(addressUrl + "api/registryRemove", accessToken, timeout, registryParam, String.class); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.client; |
| | | |
| | | import com.xxl.job.core.biz.ExecutorBiz; |
| | | import com.xxl.job.core.biz.model.*; |
| | | import com.xxl.job.core.util.XxlJobRemotingUtil; |
| | | |
| | | /** |
| | | * admin api test |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class ExecutorBizClient implements ExecutorBiz { |
| | | |
| | | public ExecutorBizClient() { |
| | | } |
| | | |
| | | public ExecutorBizClient(String addressUrl, String accessToken) { |
| | | this.addressUrl = addressUrl; |
| | | this.accessToken = accessToken; |
| | | |
| | | // valid |
| | | if (!this.addressUrl.endsWith("/")) { |
| | | this.addressUrl = this.addressUrl + "/"; |
| | | } |
| | | } |
| | | |
| | | private String addressUrl; |
| | | private String accessToken; |
| | | private int timeout = 3; |
| | | |
| | | |
| | | @Override |
| | | public ReturnT<String> beat() { |
| | | return XxlJobRemotingUtil.postBody(addressUrl + "beat", accessToken, timeout, "", String.class); |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<String> idleBeat(IdleBeatParam idleBeatParam) { |
| | | return XxlJobRemotingUtil.postBody(addressUrl + "idleBeat", accessToken, timeout, idleBeatParam, String.class); |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<String> run(TriggerParam triggerParam) { |
| | | return XxlJobRemotingUtil.postBody(addressUrl + "run", accessToken, timeout, triggerParam, String.class); |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<String> kill(KillParam killParam) { |
| | | return XxlJobRemotingUtil.postBody(addressUrl + "kill", accessToken, timeout, killParam, String.class); |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<LogResult> log(LogParam logParam) { |
| | | return XxlJobRemotingUtil.postBody(addressUrl + "log", accessToken, timeout, logParam, LogResult.class); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.impl; |
| | | |
| | | import com.xxl.job.core.biz.ExecutorBiz; |
| | | import com.xxl.job.core.biz.model.*; |
| | | import com.xxl.job.core.enums.ExecutorBlockStrategyEnum; |
| | | import com.xxl.job.core.executor.XxlJobExecutor; |
| | | import com.xxl.job.core.glue.GlueFactory; |
| | | import com.xxl.job.core.glue.GlueTypeEnum; |
| | | import com.xxl.job.core.handler.IJobHandler; |
| | | import com.xxl.job.core.handler.impl.GlueJobHandler; |
| | | import com.xxl.job.core.handler.impl.ScriptJobHandler; |
| | | import com.xxl.job.core.log.XxlJobFileAppender; |
| | | import com.xxl.job.core.thread.JobThread; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class ExecutorBizImpl implements ExecutorBiz { |
| | | private static Logger logger = LoggerFactory.getLogger(ExecutorBizImpl.class); |
| | | |
| | | @Override |
| | | public ReturnT<String> beat() { |
| | | return ReturnT.SUCCESS; |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<String> idleBeat(IdleBeatParam idleBeatParam) { |
| | | |
| | | // isRunningOrHasQueue |
| | | boolean isRunningOrHasQueue = false; |
| | | JobThread jobThread = XxlJobExecutor.loadJobThread(idleBeatParam.getJobId()); |
| | | if (jobThread != null && jobThread.isRunningOrHasQueue()) { |
| | | isRunningOrHasQueue = true; |
| | | } |
| | | |
| | | if (isRunningOrHasQueue) { |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "job thread is running or has trigger queue."); |
| | | } |
| | | return ReturnT.SUCCESS; |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<String> run(TriggerParam triggerParam) { |
| | | // load old:jobHandler + jobThread |
| | | JobThread jobThread = XxlJobExecutor.loadJobThread(triggerParam.getJobId()); |
| | | IJobHandler jobHandler = jobThread != null ? jobThread.getHandler() : null; |
| | | String removeOldReason = null; |
| | | |
| | | // valid:jobHandler + jobThread |
| | | GlueTypeEnum glueTypeEnum = GlueTypeEnum.match(triggerParam.getGlueType()); |
| | | if (GlueTypeEnum.BEAN == glueTypeEnum) { |
| | | |
| | | // new jobhandler |
| | | IJobHandler newJobHandler = XxlJobExecutor.loadJobHandler(triggerParam.getExecutorHandler()); |
| | | |
| | | // valid old jobThread |
| | | if (jobThread != null && jobHandler != newJobHandler) { |
| | | // change handler, need kill old thread |
| | | removeOldReason = "change jobhandler or glue type, and terminate the old job thread."; |
| | | |
| | | jobThread = null; |
| | | jobHandler = null; |
| | | } |
| | | |
| | | // valid handler |
| | | if (jobHandler == null) { |
| | | jobHandler = newJobHandler; |
| | | if (jobHandler == null) { |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "job handler [" + triggerParam.getExecutorHandler() + "] not found."); |
| | | } |
| | | } |
| | | |
| | | } else if (GlueTypeEnum.GLUE_GROOVY == glueTypeEnum) { |
| | | |
| | | // valid old jobThread |
| | | if (jobThread != null && |
| | | !(jobThread.getHandler() instanceof GlueJobHandler |
| | | && ((GlueJobHandler) jobThread.getHandler()).getGlueUpdatetime() == triggerParam.getGlueUpdatetime())) { |
| | | // change handler or gluesource updated, need kill old thread |
| | | removeOldReason = "change job source or glue type, and terminate the old job thread."; |
| | | |
| | | jobThread = null; |
| | | jobHandler = null; |
| | | } |
| | | |
| | | // valid handler |
| | | if (jobHandler == null) { |
| | | try { |
| | | IJobHandler originJobHandler = GlueFactory.getInstance().loadNewInstance(triggerParam.getGlueSource()); |
| | | jobHandler = new GlueJobHandler(originJobHandler, triggerParam.getGlueUpdatetime()); |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, e.getMessage()); |
| | | } |
| | | } |
| | | } else if (glueTypeEnum != null && glueTypeEnum.isScript()) { |
| | | |
| | | // valid old jobThread |
| | | if (jobThread != null && |
| | | !(jobThread.getHandler() instanceof ScriptJobHandler |
| | | && ((ScriptJobHandler) jobThread.getHandler()).getGlueUpdatetime() == triggerParam.getGlueUpdatetime())) { |
| | | // change script or gluesource updated, need kill old thread |
| | | removeOldReason = "change job source or glue type, and terminate the old job thread."; |
| | | |
| | | jobThread = null; |
| | | jobHandler = null; |
| | | } |
| | | |
| | | // valid handler |
| | | if (jobHandler == null) { |
| | | jobHandler = new ScriptJobHandler(triggerParam.getJobId(), triggerParam.getGlueUpdatetime(), triggerParam.getGlueSource(), GlueTypeEnum.match(triggerParam.getGlueType())); |
| | | } |
| | | } else { |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "glueType[" + triggerParam.getGlueType() + "] is not valid."); |
| | | } |
| | | |
| | | // executor block strategy |
| | | if (jobThread != null) { |
| | | ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum.match(triggerParam.getExecutorBlockStrategy(), null); |
| | | if (ExecutorBlockStrategyEnum.DISCARD_LATER == blockStrategy) { |
| | | // discard when running |
| | | if (jobThread.isRunningOrHasQueue()) { |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "block strategy effect:" + ExecutorBlockStrategyEnum.DISCARD_LATER.getTitle()); |
| | | } |
| | | } else if (ExecutorBlockStrategyEnum.COVER_EARLY == blockStrategy) { |
| | | // kill running jobThread |
| | | if (jobThread.isRunningOrHasQueue()) { |
| | | removeOldReason = "block strategy effect:" + ExecutorBlockStrategyEnum.COVER_EARLY.getTitle(); |
| | | |
| | | jobThread = null; |
| | | } |
| | | } else { |
| | | // just queue trigger |
| | | } |
| | | } |
| | | |
| | | // replace thread (new or exists invalid) |
| | | if (jobThread == null) { |
| | | jobThread = XxlJobExecutor.registJobThread(triggerParam.getJobId(), jobHandler, removeOldReason); |
| | | } |
| | | |
| | | // push data to queue |
| | | ReturnT<String> pushResult = jobThread.pushTriggerQueue(triggerParam); |
| | | return pushResult; |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<String> kill(KillParam killParam) { |
| | | // kill handlerThread, and create new one |
| | | JobThread jobThread = XxlJobExecutor.loadJobThread(killParam.getJobId()); |
| | | if (jobThread != null) { |
| | | XxlJobExecutor.removeJobThread(killParam.getJobId(), "scheduling center kill job."); |
| | | return ReturnT.SUCCESS; |
| | | } |
| | | |
| | | return new ReturnT<String>(ReturnT.SUCCESS_CODE, "job thread already killed."); |
| | | } |
| | | |
| | | @Override |
| | | public ReturnT<LogResult> log(LogParam logParam) { |
| | | // log filename: logPath/yyyy-MM-dd/9999.log |
| | | String logFileName = XxlJobFileAppender.makeLogFileName(new Date(logParam.getLogDateTim()), logParam.getLogId()); |
| | | |
| | | LogResult logResult = XxlJobFileAppender.readLog(logFileName, logParam.getFromLineNum()); |
| | | return new ReturnT<LogResult>(logResult); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.model; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class HandleCallbackParam implements Serializable { |
| | | private static final long serialVersionUID = 42L; |
| | | |
| | | private long logId; |
| | | private long logDateTim; |
| | | |
| | | private int handleCode; |
| | | private String handleMsg; |
| | | |
| | | public HandleCallbackParam() { |
| | | } |
| | | |
| | | public HandleCallbackParam(long logId, long logDateTim, int handleCode, String handleMsg) { |
| | | this.logId = logId; |
| | | this.logDateTim = logDateTim; |
| | | this.handleCode = handleCode; |
| | | this.handleMsg = handleMsg; |
| | | } |
| | | |
| | | public long getLogId() { |
| | | return logId; |
| | | } |
| | | |
| | | public void setLogId(long logId) { |
| | | this.logId = logId; |
| | | } |
| | | |
| | | public long getLogDateTim() { |
| | | return logDateTim; |
| | | } |
| | | |
| | | public void setLogDateTim(long logDateTim) { |
| | | this.logDateTim = logDateTim; |
| | | } |
| | | |
| | | public int getHandleCode() { |
| | | return handleCode; |
| | | } |
| | | |
| | | public void setHandleCode(int handleCode) { |
| | | this.handleCode = handleCode; |
| | | } |
| | | |
| | | public String getHandleMsg() { |
| | | return handleMsg; |
| | | } |
| | | |
| | | public void setHandleMsg(String handleMsg) { |
| | | this.handleMsg = handleMsg; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "HandleCallbackParam{" + |
| | | "logId=" + logId + |
| | | ", logDateTim=" + logDateTim + |
| | | ", handleCode=" + handleCode + |
| | | ", handleMsg='" + handleMsg + '\'' + |
| | | '}'; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.model; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class IdleBeatParam implements Serializable { |
| | | private static final long serialVersionUID = 42L; |
| | | |
| | | public IdleBeatParam() { |
| | | } |
| | | |
| | | public IdleBeatParam(int jobId) { |
| | | this.jobId = jobId; |
| | | } |
| | | |
| | | private int jobId; |
| | | |
| | | |
| | | public int getJobId() { |
| | | return jobId; |
| | | } |
| | | |
| | | public void setJobId(int jobId) { |
| | | this.jobId = jobId; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.model; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class KillParam implements Serializable { |
| | | private static final long serialVersionUID = 42L; |
| | | |
| | | public KillParam() { |
| | | } |
| | | |
| | | public KillParam(int jobId) { |
| | | this.jobId = jobId; |
| | | } |
| | | |
| | | private int jobId; |
| | | |
| | | |
| | | public int getJobId() { |
| | | return jobId; |
| | | } |
| | | |
| | | public void setJobId(int jobId) { |
| | | this.jobId = jobId; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.model; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class LogParam implements Serializable { |
| | | private static final long serialVersionUID = 42L; |
| | | |
| | | public LogParam() { |
| | | } |
| | | |
| | | public LogParam(long logDateTim, long logId, int fromLineNum) { |
| | | this.logDateTim = logDateTim; |
| | | this.logId = logId; |
| | | this.fromLineNum = fromLineNum; |
| | | } |
| | | |
| | | private long logDateTim; |
| | | private long logId; |
| | | private int fromLineNum; |
| | | |
| | | public long getLogDateTim() { |
| | | return logDateTim; |
| | | } |
| | | |
| | | public void setLogDateTim(long logDateTim) { |
| | | this.logDateTim = logDateTim; |
| | | } |
| | | |
| | | public long getLogId() { |
| | | return logId; |
| | | } |
| | | |
| | | public void setLogId(long logId) { |
| | | this.logId = logId; |
| | | } |
| | | |
| | | public int getFromLineNum() { |
| | | return fromLineNum; |
| | | } |
| | | |
| | | public void setFromLineNum(int fromLineNum) { |
| | | this.fromLineNum = fromLineNum; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.model; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class LogResult implements Serializable { |
| | | private static final long serialVersionUID = 42L; |
| | | |
| | | public LogResult() { |
| | | } |
| | | |
| | | public LogResult(int fromLineNum, int toLineNum, String logContent, boolean isEnd) { |
| | | this.fromLineNum = fromLineNum; |
| | | this.toLineNum = toLineNum; |
| | | this.logContent = logContent; |
| | | this.isEnd = isEnd; |
| | | } |
| | | |
| | | private int fromLineNum; |
| | | private int toLineNum; |
| | | private String logContent; |
| | | private boolean isEnd; |
| | | |
| | | public int getFromLineNum() { |
| | | return fromLineNum; |
| | | } |
| | | |
| | | public void setFromLineNum(int fromLineNum) { |
| | | this.fromLineNum = fromLineNum; |
| | | } |
| | | |
| | | public int getToLineNum() { |
| | | return toLineNum; |
| | | } |
| | | |
| | | public void setToLineNum(int toLineNum) { |
| | | this.toLineNum = toLineNum; |
| | | } |
| | | |
| | | public String getLogContent() { |
| | | return logContent; |
| | | } |
| | | |
| | | public void setLogContent(String logContent) { |
| | | this.logContent = logContent; |
| | | } |
| | | |
| | | public boolean isEnd() { |
| | | return isEnd; |
| | | } |
| | | |
| | | public void setEnd(boolean end) { |
| | | isEnd = end; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.model; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class RegistryParam implements Serializable { |
| | | private static final long serialVersionUID = 42L; |
| | | |
| | | private String registryGroup; |
| | | private String registryKey; |
| | | private String registryValue; |
| | | |
| | | public RegistryParam() { |
| | | } |
| | | |
| | | public RegistryParam(String registryGroup, String registryKey, String registryValue) { |
| | | this.registryGroup = registryGroup; |
| | | this.registryKey = registryKey; |
| | | this.registryValue = registryValue; |
| | | } |
| | | |
| | | public String getRegistryGroup() { |
| | | return registryGroup; |
| | | } |
| | | |
| | | public void setRegistryGroup(String registryGroup) { |
| | | this.registryGroup = registryGroup; |
| | | } |
| | | |
| | | public String getRegistryKey() { |
| | | return registryKey; |
| | | } |
| | | |
| | | public void setRegistryKey(String registryKey) { |
| | | this.registryKey = registryKey; |
| | | } |
| | | |
| | | public String getRegistryValue() { |
| | | return registryValue; |
| | | } |
| | | |
| | | public void setRegistryValue(String registryValue) { |
| | | this.registryValue = registryValue; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "RegistryParam{" + |
| | | "registryGroup='" + registryGroup + '\'' + |
| | | ", registryKey='" + registryKey + '\'' + |
| | | ", registryValue='" + registryValue + '\'' + |
| | | '}'; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.model; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * common return |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class ReturnT<T> implements Serializable { |
| | | public static final long serialVersionUID = 42L; |
| | | |
| | | public static final int SUCCESS_CODE = 200; |
| | | public static final int FAIL_CODE = 500; |
| | | |
| | | public static final ReturnT<String> SUCCESS = new ReturnT<String>(null); |
| | | public static final ReturnT<String> FAIL = new ReturnT<String>(FAIL_CODE, null); |
| | | |
| | | private int code; |
| | | private String msg; |
| | | private T content; |
| | | |
| | | public ReturnT() { |
| | | } |
| | | |
| | | public ReturnT(int code, String msg) { |
| | | this.code = code; |
| | | this.msg = msg; |
| | | } |
| | | |
| | | public ReturnT(T content) { |
| | | this.code = SUCCESS_CODE; |
| | | this.content = content; |
| | | } |
| | | |
| | | public int getCode() { |
| | | return code; |
| | | } |
| | | |
| | | public void setCode(int code) { |
| | | this.code = code; |
| | | } |
| | | |
| | | public String getMsg() { |
| | | return msg; |
| | | } |
| | | |
| | | public void setMsg(String msg) { |
| | | this.msg = msg; |
| | | } |
| | | |
| | | public T getContent() { |
| | | return content; |
| | | } |
| | | |
| | | public void setContent(T content) { |
| | | this.content = content; |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "ReturnT [code=" + code + ", msg=" + msg + ", content=" + content + "]"; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.biz.model; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class TriggerParam implements Serializable { |
| | | private static final long serialVersionUID = 42L; |
| | | |
| | | private int jobId; |
| | | |
| | | private String executorHandler; |
| | | private String executorParams; |
| | | private String executorBlockStrategy; |
| | | private int executorTimeout; |
| | | |
| | | private long logId; |
| | | private long logDateTime; |
| | | |
| | | private String glueType; |
| | | private String glueSource; |
| | | private long glueUpdatetime; |
| | | |
| | | private int broadcastIndex; |
| | | private int broadcastTotal; |
| | | |
| | | |
| | | public int getJobId() { |
| | | return jobId; |
| | | } |
| | | |
| | | public void setJobId(int jobId) { |
| | | this.jobId = jobId; |
| | | } |
| | | |
| | | public String getExecutorHandler() { |
| | | return executorHandler; |
| | | } |
| | | |
| | | public void setExecutorHandler(String executorHandler) { |
| | | this.executorHandler = executorHandler; |
| | | } |
| | | |
| | | public String getExecutorParams() { |
| | | return executorParams; |
| | | } |
| | | |
| | | public void setExecutorParams(String executorParams) { |
| | | this.executorParams = executorParams; |
| | | } |
| | | |
| | | public String getExecutorBlockStrategy() { |
| | | return executorBlockStrategy; |
| | | } |
| | | |
| | | public void setExecutorBlockStrategy(String executorBlockStrategy) { |
| | | this.executorBlockStrategy = executorBlockStrategy; |
| | | } |
| | | |
| | | public int getExecutorTimeout() { |
| | | return executorTimeout; |
| | | } |
| | | |
| | | public void setExecutorTimeout(int executorTimeout) { |
| | | this.executorTimeout = executorTimeout; |
| | | } |
| | | |
| | | public long getLogId() { |
| | | return logId; |
| | | } |
| | | |
| | | public void setLogId(long logId) { |
| | | this.logId = logId; |
| | | } |
| | | |
| | | public long getLogDateTime() { |
| | | return logDateTime; |
| | | } |
| | | |
| | | public void setLogDateTime(long logDateTime) { |
| | | this.logDateTime = logDateTime; |
| | | } |
| | | |
| | | public String getGlueType() { |
| | | return glueType; |
| | | } |
| | | |
| | | public void setGlueType(String glueType) { |
| | | this.glueType = glueType; |
| | | } |
| | | |
| | | public String getGlueSource() { |
| | | return glueSource; |
| | | } |
| | | |
| | | public void setGlueSource(String glueSource) { |
| | | this.glueSource = glueSource; |
| | | } |
| | | |
| | | public long getGlueUpdatetime() { |
| | | return glueUpdatetime; |
| | | } |
| | | |
| | | public void setGlueUpdatetime(long glueUpdatetime) { |
| | | this.glueUpdatetime = glueUpdatetime; |
| | | } |
| | | |
| | | public int getBroadcastIndex() { |
| | | return broadcastIndex; |
| | | } |
| | | |
| | | public void setBroadcastIndex(int broadcastIndex) { |
| | | this.broadcastIndex = broadcastIndex; |
| | | } |
| | | |
| | | public int getBroadcastTotal() { |
| | | return broadcastTotal; |
| | | } |
| | | |
| | | public void setBroadcastTotal(int broadcastTotal) { |
| | | this.broadcastTotal = broadcastTotal; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "TriggerParam{" + |
| | | "jobId=" + jobId + |
| | | ", executorHandler='" + executorHandler + '\'' + |
| | | ", executorParams='" + executorParams + '\'' + |
| | | ", executorBlockStrategy='" + executorBlockStrategy + '\'' + |
| | | ", executorTimeout=" + executorTimeout + |
| | | ", logId=" + logId + |
| | | ", logDateTime=" + logDateTime + |
| | | ", glueType='" + glueType + '\'' + |
| | | ", glueSource='" + glueSource + '\'' + |
| | | ", glueUpdatetime=" + glueUpdatetime + |
| | | ", broadcastIndex=" + broadcastIndex + |
| | | ", broadcastTotal=" + broadcastTotal + |
| | | '}'; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.context; |
| | | |
| | | /** |
| | | * xxl-job context |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class XxlJobContext { |
| | | |
| | | public static final int HANDLE_CODE_SUCCESS = 200; |
| | | public static final int HANDLE_CODE_FAIL = 500; |
| | | public static final int HANDLE_CODE_TIMEOUT = 502; |
| | | |
| | | // ---------------------- base info ---------------------- |
| | | |
| | | /** |
| | | * job id |
| | | */ |
| | | private final long jobId; |
| | | |
| | | /** |
| | | * job param |
| | | */ |
| | | private final String jobParam; |
| | | |
| | | // ---------------------- for log ---------------------- |
| | | |
| | | /** |
| | | * job log filename |
| | | */ |
| | | private final String jobLogFileName; |
| | | |
| | | // ---------------------- for shard ---------------------- |
| | | |
| | | /** |
| | | * shard index |
| | | */ |
| | | private final int shardIndex; |
| | | |
| | | /** |
| | | * shard total |
| | | */ |
| | | private final int shardTotal; |
| | | |
| | | // ---------------------- for handle ---------------------- |
| | | |
| | | /** |
| | | * handleCode:The result status of job execution |
| | | * <p> |
| | | * 200 : success |
| | | * 500 : fail |
| | | * 502 : timeout |
| | | */ |
| | | private int handleCode; |
| | | |
| | | /** |
| | | * handleMsg:The simple log msg of job execution |
| | | */ |
| | | private String handleMsg; |
| | | |
| | | public XxlJobContext(long jobId, String jobParam, String jobLogFileName, int shardIndex, int shardTotal) { |
| | | this.jobId = jobId; |
| | | this.jobParam = jobParam; |
| | | this.jobLogFileName = jobLogFileName; |
| | | this.shardIndex = shardIndex; |
| | | this.shardTotal = shardTotal; |
| | | |
| | | this.handleCode = HANDLE_CODE_SUCCESS; // default success |
| | | } |
| | | |
| | | public long getJobId() { |
| | | return jobId; |
| | | } |
| | | |
| | | public String getJobParam() { |
| | | return jobParam; |
| | | } |
| | | |
| | | public String getJobLogFileName() { |
| | | return jobLogFileName; |
| | | } |
| | | |
| | | public int getShardIndex() { |
| | | return shardIndex; |
| | | } |
| | | |
| | | public int getShardTotal() { |
| | | return shardTotal; |
| | | } |
| | | |
| | | public void setHandleCode(int handleCode) { |
| | | this.handleCode = handleCode; |
| | | } |
| | | |
| | | public int getHandleCode() { |
| | | return handleCode; |
| | | } |
| | | |
| | | public void setHandleMsg(String handleMsg) { |
| | | this.handleMsg = handleMsg; |
| | | } |
| | | |
| | | public String getHandleMsg() { |
| | | return handleMsg; |
| | | } |
| | | |
| | | // ---------------------- tool ---------------------- |
| | | |
| | | private static InheritableThreadLocal<XxlJobContext> contextHolder = new InheritableThreadLocal<XxlJobContext>(); // support for child thread of job handler) |
| | | |
| | | public static void setXxlJobContext(XxlJobContext xxlJobContext) { |
| | | contextHolder.set(xxlJobContext); |
| | | } |
| | | |
| | | public static XxlJobContext getXxlJobContext() { |
| | | return contextHolder.get(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.context; |
| | | |
| | | import com.xxl.job.core.log.XxlJobFileAppender; |
| | | import com.xxl.job.core.util.DateUtil; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.slf4j.helpers.FormattingTuple; |
| | | import org.slf4j.helpers.MessageFormatter; |
| | | |
| | | import java.io.PrintWriter; |
| | | import java.io.StringWriter; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * helper for xxl-job |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class XxlJobHelper { |
| | | |
| | | // ---------------------- base info ---------------------- |
| | | |
| | | /** |
| | | * current JobId |
| | | * |
| | | * @return |
| | | */ |
| | | public static long getJobId() { |
| | | XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext(); |
| | | if (xxlJobContext == null) { |
| | | return -1; |
| | | } |
| | | |
| | | return xxlJobContext.getJobId(); |
| | | } |
| | | |
| | | /** |
| | | * current JobParam |
| | | * |
| | | * @return |
| | | */ |
| | | public static String getJobParam() { |
| | | XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext(); |
| | | if (xxlJobContext == null) { |
| | | return null; |
| | | } |
| | | |
| | | return xxlJobContext.getJobParam(); |
| | | } |
| | | |
| | | // ---------------------- for log ---------------------- |
| | | |
| | | /** |
| | | * current JobLogFileName |
| | | * |
| | | * @return |
| | | */ |
| | | public static String getJobLogFileName() { |
| | | XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext(); |
| | | if (xxlJobContext == null) { |
| | | return null; |
| | | } |
| | | |
| | | return xxlJobContext.getJobLogFileName(); |
| | | } |
| | | |
| | | // ---------------------- for shard ---------------------- |
| | | |
| | | /** |
| | | * current ShardIndex |
| | | * |
| | | * @return |
| | | */ |
| | | public static int getShardIndex() { |
| | | XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext(); |
| | | if (xxlJobContext == null) { |
| | | return -1; |
| | | } |
| | | |
| | | return xxlJobContext.getShardIndex(); |
| | | } |
| | | |
| | | /** |
| | | * current ShardTotal |
| | | * |
| | | * @return |
| | | */ |
| | | public static int getShardTotal() { |
| | | XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext(); |
| | | if (xxlJobContext == null) { |
| | | return -1; |
| | | } |
| | | |
| | | return xxlJobContext.getShardTotal(); |
| | | } |
| | | |
| | | // ---------------------- tool for log ---------------------- |
| | | |
| | | private static Logger logger = LoggerFactory.getLogger("xxl-job logger"); |
| | | |
| | | /** |
| | | * append log with pattern |
| | | * |
| | | * @param appendLogPattern like "aaa {} bbb {} ccc" |
| | | * @param appendLogArguments like "111, true" |
| | | */ |
| | | public static boolean log(String appendLogPattern, Object... appendLogArguments) { |
| | | |
| | | FormattingTuple ft = MessageFormatter.arrayFormat(appendLogPattern, appendLogArguments); |
| | | String appendLog = ft.getMessage(); |
| | | |
| | | /*appendLog = appendLogPattern; |
| | | if (appendLogArguments!=null && appendLogArguments.length>0) { |
| | | appendLog = MessageFormat.format(appendLogPattern, appendLogArguments); |
| | | }*/ |
| | | |
| | | StackTraceElement callInfo = new Throwable().getStackTrace()[1]; |
| | | return logDetail(callInfo, appendLog); |
| | | } |
| | | |
| | | /** |
| | | * append exception stack |
| | | * |
| | | * @param e |
| | | */ |
| | | public static boolean log(Throwable e) { |
| | | |
| | | StringWriter stringWriter = new StringWriter(); |
| | | e.printStackTrace(new PrintWriter(stringWriter)); |
| | | String appendLog = stringWriter.toString(); |
| | | |
| | | StackTraceElement callInfo = new Throwable().getStackTrace()[1]; |
| | | return logDetail(callInfo, appendLog); |
| | | } |
| | | |
| | | /** |
| | | * append log |
| | | * |
| | | * @param callInfo |
| | | * @param appendLog |
| | | */ |
| | | private static boolean logDetail(StackTraceElement callInfo, String appendLog) { |
| | | XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext(); |
| | | if (xxlJobContext == null) { |
| | | return false; |
| | | } |
| | | |
| | | /*// "yyyy-MM-dd HH:mm:ss [ClassName]-[MethodName]-[LineNumber]-[ThreadName] log"; |
| | | StackTraceElement[] stackTraceElements = new Throwable().getStackTrace(); |
| | | StackTraceElement callInfo = stackTraceElements[1];*/ |
| | | |
| | | StringBuffer stringBuffer = new StringBuffer(); |
| | | stringBuffer.append(DateUtil.formatDateTime(new Date())).append(" ") |
| | | .append("[" + callInfo.getClassName() + "#" + callInfo.getMethodName() + "]").append("-") |
| | | .append("[" + callInfo.getLineNumber() + "]").append("-") |
| | | .append("[" + Thread.currentThread().getName() + "]").append(" ") |
| | | .append(appendLog != null ? appendLog : ""); |
| | | String formatAppendLog = stringBuffer.toString(); |
| | | |
| | | // appendlog |
| | | String logFileName = xxlJobContext.getJobLogFileName(); |
| | | |
| | | if (logFileName != null && logFileName.trim().length() > 0) { |
| | | XxlJobFileAppender.appendLog(logFileName, formatAppendLog); |
| | | return true; |
| | | } else { |
| | | logger.info(">>>>>>>>>>> {}", formatAppendLog); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // ---------------------- tool for handleResult ---------------------- |
| | | |
| | | /** |
| | | * handle success |
| | | * |
| | | * @return |
| | | */ |
| | | public static boolean handleSuccess() { |
| | | return handleResult(XxlJobContext.HANDLE_CODE_SUCCESS, null); |
| | | } |
| | | |
| | | /** |
| | | * handle success with log msg |
| | | * |
| | | * @param handleMsg |
| | | * @return |
| | | */ |
| | | public static boolean handleSuccess(String handleMsg) { |
| | | return handleResult(XxlJobContext.HANDLE_CODE_SUCCESS, handleMsg); |
| | | } |
| | | |
| | | /** |
| | | * handle fail |
| | | * |
| | | * @return |
| | | */ |
| | | public static boolean handleFail() { |
| | | return handleResult(XxlJobContext.HANDLE_CODE_FAIL, null); |
| | | } |
| | | |
| | | /** |
| | | * handle fail with log msg |
| | | * |
| | | * @param handleMsg |
| | | * @return |
| | | */ |
| | | public static boolean handleFail(String handleMsg) { |
| | | return handleResult(XxlJobContext.HANDLE_CODE_FAIL, handleMsg); |
| | | } |
| | | |
| | | /** |
| | | * handle timeout |
| | | * |
| | | * @return |
| | | */ |
| | | public static boolean handleTimeout() { |
| | | return handleResult(XxlJobContext.HANDLE_CODE_TIMEOUT, null); |
| | | } |
| | | |
| | | /** |
| | | * handle timeout with log msg |
| | | * |
| | | * @param handleMsg |
| | | * @return |
| | | */ |
| | | public static boolean handleTimeout(String handleMsg) { |
| | | return handleResult(XxlJobContext.HANDLE_CODE_TIMEOUT, handleMsg); |
| | | } |
| | | |
| | | /** |
| | | * @param handleCode 200 : success |
| | | * 500 : fail |
| | | * 502 : timeout |
| | | * @param handleMsg |
| | | * @return |
| | | */ |
| | | public static boolean handleResult(int handleCode, String handleMsg) { |
| | | XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext(); |
| | | if (xxlJobContext == null) { |
| | | return false; |
| | | } |
| | | |
| | | xxlJobContext.setHandleCode(handleCode); |
| | | if (handleMsg != null) { |
| | | xxlJobContext.setHandleMsg(handleMsg); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.enums; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public enum ExecutorBlockStrategyEnum { |
| | | |
| | | SERIAL_EXECUTION("Serial execution"), |
| | | /*CONCURRENT_EXECUTION("并行"),*/ |
| | | DISCARD_LATER("Discard Later"), |
| | | COVER_EARLY("Cover Early"); |
| | | |
| | | private String title; |
| | | |
| | | private ExecutorBlockStrategyEnum(String title) { |
| | | this.title = title; |
| | | } |
| | | |
| | | public void setTitle(String title) { |
| | | this.title = title; |
| | | } |
| | | |
| | | public String getTitle() { |
| | | return title; |
| | | } |
| | | |
| | | public static ExecutorBlockStrategyEnum match(String name, ExecutorBlockStrategyEnum defaultItem) { |
| | | if (name != null) { |
| | | for (ExecutorBlockStrategyEnum item : ExecutorBlockStrategyEnum.values()) { |
| | | if (item.name().equals(name)) { |
| | | return item; |
| | | } |
| | | } |
| | | } |
| | | return defaultItem; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.enums; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class RegistryConfig { |
| | | |
| | | public static final int BEAT_TIMEOUT = 30; |
| | | public static final int DEAD_TIMEOUT = BEAT_TIMEOUT * 3; |
| | | |
| | | public enum RegistType {EXECUTOR, ADMIN} |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.executor; |
| | | |
| | | import com.xxl.job.core.biz.AdminBiz; |
| | | import com.xxl.job.core.biz.client.AdminBizClient; |
| | | import com.xxl.job.core.handler.IJobHandler; |
| | | import com.xxl.job.core.handler.annotation.XxlJob; |
| | | import com.xxl.job.core.handler.impl.MethodJobHandler; |
| | | import com.xxl.job.core.log.XxlJobFileAppender; |
| | | import com.xxl.job.core.server.EmbedServer; |
| | | import com.xxl.job.core.thread.JobLogFileCleanThread; |
| | | import com.xxl.job.core.thread.JobThread; |
| | | import com.xxl.job.core.thread.TriggerCallbackThread; |
| | | import com.xxl.job.core.util.IpUtil; |
| | | import com.xxl.job.core.util.NetUtil; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.lang.reflect.Method; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.ConcurrentMap; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class XxlJobExecutor { |
| | | private static final Logger logger = LoggerFactory.getLogger(XxlJobExecutor.class); |
| | | |
| | | // ---------------------- param ---------------------- |
| | | private String adminAddresses; |
| | | private String accessToken; |
| | | private String appname; |
| | | private String address; |
| | | private String ip; |
| | | private int port; |
| | | private String logPath; |
| | | private int logRetentionDays; |
| | | |
| | | public void setAdminAddresses(String adminAddresses) { |
| | | this.adminAddresses = adminAddresses; |
| | | } |
| | | |
| | | public void setAccessToken(String accessToken) { |
| | | this.accessToken = accessToken; |
| | | } |
| | | |
| | | public void setAppname(String appname) { |
| | | this.appname = appname; |
| | | } |
| | | |
| | | public void setAddress(String address) { |
| | | this.address = address; |
| | | } |
| | | |
| | | public void setIp(String ip) { |
| | | this.ip = ip; |
| | | } |
| | | |
| | | public void setPort(int port) { |
| | | this.port = port; |
| | | } |
| | | |
| | | public void setLogPath(String logPath) { |
| | | this.logPath = logPath; |
| | | } |
| | | |
| | | public void setLogRetentionDays(int logRetentionDays) { |
| | | this.logRetentionDays = logRetentionDays; |
| | | } |
| | | |
| | | // ---------------------- start + stop ---------------------- |
| | | public void start() throws Exception { |
| | | |
| | | // init logpath |
| | | XxlJobFileAppender.initLogPath(logPath); |
| | | |
| | | // init invoker, admin-client |
| | | initAdminBizList(adminAddresses, accessToken); |
| | | |
| | | // init JobLogFileCleanThread |
| | | JobLogFileCleanThread.getInstance().start(logRetentionDays); |
| | | |
| | | // init TriggerCallbackThread |
| | | TriggerCallbackThread.getInstance().start(); |
| | | |
| | | // init executor-server |
| | | initEmbedServer(address, ip, port, appname, accessToken); |
| | | } |
| | | |
| | | public void destroy() { |
| | | // destroy executor-server |
| | | stopEmbedServer(); |
| | | |
| | | // destroy jobThreadRepository |
| | | if (jobThreadRepository.size() > 0) { |
| | | for (Map.Entry<Integer, JobThread> item : jobThreadRepository.entrySet()) { |
| | | JobThread oldJobThread = removeJobThread(item.getKey(), "web container destroy and kill the job."); |
| | | // wait for job thread push result to callback queue |
| | | if (oldJobThread != null) { |
| | | try { |
| | | oldJobThread.join(); |
| | | } catch (InterruptedException e) { |
| | | logger.error(">>>>>>>>>>> xxl-job, JobThread destroy(join) error, jobId:{}", item.getKey(), e); |
| | | } |
| | | } |
| | | } |
| | | jobThreadRepository.clear(); |
| | | } |
| | | jobHandlerRepository.clear(); |
| | | |
| | | // destroy JobLogFileCleanThread |
| | | JobLogFileCleanThread.getInstance().toStop(); |
| | | |
| | | // destroy TriggerCallbackThread |
| | | TriggerCallbackThread.getInstance().toStop(); |
| | | |
| | | } |
| | | |
| | | // ---------------------- admin-client (rpc invoker) ---------------------- |
| | | private static List<AdminBiz> adminBizList; |
| | | |
| | | private void initAdminBizList(String adminAddresses, String accessToken) throws Exception { |
| | | if (adminAddresses != null && adminAddresses.trim().length() > 0) { |
| | | for (String address : adminAddresses.trim().split(",")) { |
| | | if (address != null && address.trim().length() > 0) { |
| | | |
| | | AdminBiz adminBiz = new AdminBizClient(address.trim(), accessToken); |
| | | |
| | | if (adminBizList == null) { |
| | | adminBizList = new ArrayList<AdminBiz>(); |
| | | } |
| | | adminBizList.add(adminBiz); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | public static List<AdminBiz> getAdminBizList() { |
| | | return adminBizList; |
| | | } |
| | | |
| | | // ---------------------- executor-server (rpc provider) ---------------------- |
| | | private EmbedServer embedServer = null; |
| | | |
| | | private void initEmbedServer(String address, String ip, int port, String appname, String accessToken) throws Exception { |
| | | |
| | | // fill ip port |
| | | port = port > 0 ? port : NetUtil.findAvailablePort(9999); |
| | | ip = (ip != null && ip.trim().length() > 0) ? ip : IpUtil.getIp(); |
| | | |
| | | // generate address |
| | | if (address == null || address.trim().length() == 0) { |
| | | String ip_port_address = IpUtil.getIpPort(ip, port); // registry-address:default use address to registry , otherwise use ip:port if address is null |
| | | address = "http://{ip_port}/".replace("{ip_port}", ip_port_address); |
| | | } |
| | | |
| | | // accessToken |
| | | if (accessToken == null || accessToken.trim().length() == 0) { |
| | | logger.warn(">>>>>>>>>>> xxl-job accessToken is empty. To ensure system security, please set the accessToken."); |
| | | } |
| | | |
| | | // start |
| | | embedServer = new EmbedServer(); |
| | | embedServer.start(address, port, appname, accessToken); |
| | | } |
| | | |
| | | private void stopEmbedServer() { |
| | | // stop provider factory |
| | | if (embedServer != null) { |
| | | try { |
| | | embedServer.stop(); |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ---------------------- job handler repository ---------------------- |
| | | private static ConcurrentMap<String, IJobHandler> jobHandlerRepository = new ConcurrentHashMap<String, IJobHandler>(); |
| | | |
| | | public static IJobHandler loadJobHandler(String name) { |
| | | return jobHandlerRepository.get(name); |
| | | } |
| | | |
| | | public static IJobHandler registJobHandler(String name, IJobHandler jobHandler) { |
| | | logger.info(">>>>>>>>>>> xxl-job register jobhandler success, name:{}, jobHandler:{}", name, jobHandler); |
| | | return jobHandlerRepository.put(name, jobHandler); |
| | | } |
| | | |
| | | protected void registJobHandler(XxlJob xxlJob, Object bean, Method executeMethod) { |
| | | if (xxlJob == null) { |
| | | return; |
| | | } |
| | | |
| | | String name = xxlJob.value(); |
| | | //make and simplify the variables since they'll be called several times later |
| | | Class<?> clazz = bean.getClass(); |
| | | String methodName = executeMethod.getName(); |
| | | if (name.trim().length() == 0) { |
| | | throw new RuntimeException("xxl-job method-jobhandler name invalid, for[" + clazz + "#" + methodName + "] ."); |
| | | } |
| | | if (loadJobHandler(name) != null) { |
| | | throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts."); |
| | | } |
| | | |
| | | // execute method |
| | | /*if (!(method.getParameterTypes().length == 1 && method.getParameterTypes()[0].isAssignableFrom(String.class))) { |
| | | throw new RuntimeException("xxl-job method-jobhandler param-classtype invalid, for[" + bean.getClass() + "#" + method.getName() + "] , " + |
| | | "The correct method format like \" public ReturnT<String> execute(String param) \" ."); |
| | | } |
| | | if (!method.getReturnType().isAssignableFrom(ReturnT.class)) { |
| | | throw new RuntimeException("xxl-job method-jobhandler return-classtype invalid, for[" + bean.getClass() + "#" + method.getName() + "] , " + |
| | | "The correct method format like \" public ReturnT<String> execute(String param) \" ."); |
| | | }*/ |
| | | |
| | | executeMethod.setAccessible(true); |
| | | |
| | | // init and destroy |
| | | Method initMethod = null; |
| | | Method destroyMethod = null; |
| | | |
| | | if (xxlJob.init().trim().length() > 0) { |
| | | try { |
| | | initMethod = clazz.getDeclaredMethod(xxlJob.init()); |
| | | initMethod.setAccessible(true); |
| | | } catch (NoSuchMethodException e) { |
| | | throw new RuntimeException("xxl-job method-jobhandler initMethod invalid, for[" + clazz + "#" + methodName + "] ."); |
| | | } |
| | | } |
| | | if (xxlJob.destroy().trim().length() > 0) { |
| | | try { |
| | | destroyMethod = clazz.getDeclaredMethod(xxlJob.destroy()); |
| | | destroyMethod.setAccessible(true); |
| | | } catch (NoSuchMethodException e) { |
| | | throw new RuntimeException("xxl-job method-jobhandler destroyMethod invalid, for[" + clazz + "#" + methodName + "] ."); |
| | | } |
| | | } |
| | | |
| | | // registry jobhandler |
| | | registJobHandler(name, new MethodJobHandler(bean, executeMethod, initMethod, destroyMethod)); |
| | | |
| | | } |
| | | |
| | | // ---------------------- job thread repository ---------------------- |
| | | private static ConcurrentMap<Integer, JobThread> jobThreadRepository = new ConcurrentHashMap<Integer, JobThread>(); |
| | | |
| | | public static JobThread registJobThread(int jobId, IJobHandler handler, String removeOldReason) { |
| | | JobThread newJobThread = new JobThread(jobId, handler); |
| | | newJobThread.start(); |
| | | logger.info(">>>>>>>>>>> xxl-job regist JobThread success, jobId:{}, handler:{}", new Object[]{jobId, handler}); |
| | | |
| | | JobThread oldJobThread = jobThreadRepository.put(jobId, newJobThread); // putIfAbsent | oh my god, map's put method return the old value!!! |
| | | if (oldJobThread != null) { |
| | | oldJobThread.toStop(removeOldReason); |
| | | oldJobThread.interrupt(); |
| | | } |
| | | |
| | | return newJobThread; |
| | | } |
| | | |
| | | public static JobThread removeJobThread(int jobId, String removeOldReason) { |
| | | JobThread oldJobThread = jobThreadRepository.remove(jobId); |
| | | if (oldJobThread != null) { |
| | | oldJobThread.toStop(removeOldReason); |
| | | oldJobThread.interrupt(); |
| | | |
| | | return oldJobThread; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | public static JobThread loadJobThread(int jobId) { |
| | | JobThread jobThread = jobThreadRepository.get(jobId); |
| | | return jobThread; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.executor.impl; |
| | | |
| | | import com.xxl.job.core.executor.XxlJobExecutor; |
| | | import com.xxl.job.core.handler.annotation.XxlJob; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.lang.reflect.Method; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | |
| | | /** |
| | | * xxl-job executor (for frameless) |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class XxlJobSimpleExecutor extends XxlJobExecutor { |
| | | private static final Logger logger = LoggerFactory.getLogger(XxlJobSimpleExecutor.class); |
| | | |
| | | |
| | | private List<Object> xxlJobBeanList = new ArrayList<>(); |
| | | |
| | | public List<Object> getXxlJobBeanList() { |
| | | return xxlJobBeanList; |
| | | } |
| | | |
| | | public void setXxlJobBeanList(List<Object> xxlJobBeanList) { |
| | | this.xxlJobBeanList = xxlJobBeanList; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void start() { |
| | | |
| | | // init JobHandler Repository (for method) |
| | | initJobHandlerMethodRepository(xxlJobBeanList); |
| | | |
| | | // super start |
| | | try { |
| | | super.start(); |
| | | } catch (Exception e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void destroy() { |
| | | super.destroy(); |
| | | } |
| | | |
| | | |
| | | private void initJobHandlerMethodRepository(List<Object> xxlJobBeanList) { |
| | | if (xxlJobBeanList == null || xxlJobBeanList.size() == 0) { |
| | | return; |
| | | } |
| | | |
| | | // init job handler from method |
| | | for (Object bean : xxlJobBeanList) { |
| | | // method |
| | | Method[] methods = bean.getClass().getDeclaredMethods(); |
| | | if (methods.length == 0) { |
| | | continue; |
| | | } |
| | | for (Method executeMethod : methods) { |
| | | XxlJob xxlJob = executeMethod.getAnnotation(XxlJob.class); |
| | | // registry |
| | | registJobHandler(xxlJob, bean, executeMethod); |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.executor.impl; |
| | | |
| | | import com.xxl.job.core.executor.XxlJobExecutor; |
| | | import com.xxl.job.core.glue.GlueFactory; |
| | | import com.xxl.job.core.handler.annotation.XxlJob; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.BeansException; |
| | | import org.springframework.beans.factory.DisposableBean; |
| | | import org.springframework.beans.factory.SmartInitializingSingleton; |
| | | import org.springframework.context.ApplicationContext; |
| | | import org.springframework.context.ApplicationContextAware; |
| | | import org.springframework.core.MethodIntrospector; |
| | | import org.springframework.core.annotation.AnnotatedElementUtils; |
| | | |
| | | import java.lang.reflect.Method; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * xxl-job executor (for spring) |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean { |
| | | private static final Logger logger = LoggerFactory.getLogger(XxlJobSpringExecutor.class); |
| | | |
| | | // start |
| | | @Override |
| | | public void afterSingletonsInstantiated() { |
| | | |
| | | // init JobHandler Repository |
| | | /*initJobHandlerRepository(applicationContext);*/ |
| | | |
| | | // init JobHandler Repository (for method) |
| | | initJobHandlerMethodRepository(applicationContext); |
| | | |
| | | // refresh GlueFactory |
| | | GlueFactory.refreshInstance(1); |
| | | |
| | | // super start |
| | | try { |
| | | super.start(); |
| | | } catch (Exception e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | } |
| | | |
| | | // destroy |
| | | @Override |
| | | public void destroy() { |
| | | super.destroy(); |
| | | } |
| | | |
| | | |
| | | /*private void initJobHandlerRepository(ApplicationContext applicationContext) { |
| | | if (applicationContext == null) { |
| | | return; |
| | | } |
| | | |
| | | // init job handler action |
| | | Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class); |
| | | |
| | | if (serviceBeanMap != null && serviceBeanMap.size() > 0) { |
| | | for (Object serviceBean : serviceBeanMap.values()) { |
| | | if (serviceBean instanceof IJobHandler) { |
| | | String name = serviceBean.getClass().getAnnotation(JobHandler.class).value(); |
| | | IJobHandler handler = (IJobHandler) serviceBean; |
| | | if (loadJobHandler(name) != null) { |
| | | throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts."); |
| | | } |
| | | registJobHandler(name, handler); |
| | | } |
| | | } |
| | | } |
| | | }*/ |
| | | |
| | | private void initJobHandlerMethodRepository(ApplicationContext applicationContext) { |
| | | if (applicationContext == null) { |
| | | return; |
| | | } |
| | | // init job handler from method |
| | | String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true); |
| | | for (String beanDefinitionName : beanDefinitionNames) { |
| | | Object bean = applicationContext.getBean(beanDefinitionName); |
| | | |
| | | Map<Method, XxlJob> annotatedMethods = null; // referred to :org.springframework.context.event.EventListenerMethodProcessor.processBean |
| | | try { |
| | | annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(), |
| | | new MethodIntrospector.MetadataLookup<XxlJob>() { |
| | | @Override |
| | | public XxlJob inspect(Method method) { |
| | | return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class); |
| | | } |
| | | }); |
| | | } catch (Throwable ex) { |
| | | logger.error("xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex); |
| | | } |
| | | if (annotatedMethods == null || annotatedMethods.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) { |
| | | Method executeMethod = methodXxlJobEntry.getKey(); |
| | | XxlJob xxlJob = methodXxlJobEntry.getValue(); |
| | | // regist |
| | | registJobHandler(xxlJob, bean, executeMethod); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ---------------------- applicationContext ---------------------- |
| | | private static ApplicationContext applicationContext; |
| | | |
| | | @Override |
| | | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
| | | XxlJobSpringExecutor.applicationContext = applicationContext; |
| | | } |
| | | |
| | | public static ApplicationContext getApplicationContext() { |
| | | return applicationContext; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.glue; |
| | | |
| | | import com.xxl.job.core.glue.impl.SpringGlueFactory; |
| | | import com.xxl.job.core.handler.IJobHandler; |
| | | import groovy.lang.GroovyClassLoader; |
| | | |
| | | import java.math.BigInteger; |
| | | import java.security.MessageDigest; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.ConcurrentMap; |
| | | |
| | | /** |
| | | * glue factory, product class/object by name |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class GlueFactory { |
| | | |
| | | private static GlueFactory glueFactory = new GlueFactory(); |
| | | |
| | | public static GlueFactory getInstance() { |
| | | return glueFactory; |
| | | } |
| | | |
| | | public static void refreshInstance(int type) { |
| | | if (type == 0) { |
| | | glueFactory = new GlueFactory(); |
| | | } else if (type == 1) { |
| | | glueFactory = new SpringGlueFactory(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * groovy class loader |
| | | */ |
| | | private GroovyClassLoader groovyClassLoader = new GroovyClassLoader(); |
| | | private ConcurrentMap<String, Class<?>> CLASS_CACHE = new ConcurrentHashMap<>(); |
| | | |
| | | /** |
| | | * load new instance, prototype |
| | | * |
| | | * @param codeSource |
| | | * @return |
| | | * @throws Exception |
| | | */ |
| | | public IJobHandler loadNewInstance(String codeSource) throws Exception { |
| | | if (codeSource != null && codeSource.trim().length() > 0) { |
| | | Class<?> clazz = getCodeSourceClass(codeSource); |
| | | if (clazz != null) { |
| | | Object instance = clazz.newInstance(); |
| | | if (instance != null) { |
| | | if (instance instanceof IJobHandler) { |
| | | this.injectService(instance); |
| | | return (IJobHandler) instance; |
| | | } else { |
| | | throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadNewInstance error, " |
| | | + "cannot convert from instance[" + instance.getClass() + "] to IJobHandler"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | throw new IllegalArgumentException(">>>>>>>>>>> xxl-glue, loadNewInstance error, instance is null"); |
| | | } |
| | | |
| | | private Class<?> getCodeSourceClass(String codeSource) { |
| | | try { |
| | | // md5 |
| | | byte[] md5 = MessageDigest.getInstance("MD5").digest(codeSource.getBytes()); |
| | | String md5Str = new BigInteger(1, md5).toString(16); |
| | | |
| | | Class<?> clazz = CLASS_CACHE.get(md5Str); |
| | | if (clazz == null) { |
| | | clazz = groovyClassLoader.parseClass(codeSource); |
| | | CLASS_CACHE.putIfAbsent(md5Str, clazz); |
| | | } |
| | | return clazz; |
| | | } catch (Exception e) { |
| | | return groovyClassLoader.parseClass(codeSource); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * inject service of bean field |
| | | * |
| | | * @param instance |
| | | */ |
| | | public void injectService(Object instance) { |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.glue; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public enum GlueTypeEnum { |
| | | |
| | | BEAN("BEAN", false, null, null), |
| | | GLUE_GROOVY("GLUE(Java)", false, null, null), |
| | | GLUE_SHELL("GLUE(Shell)", true, "bash", ".sh"), |
| | | GLUE_PYTHON("GLUE(Python)", true, "python", ".py"), |
| | | GLUE_PHP("GLUE(PHP)", true, "php", ".php"), |
| | | GLUE_NODEJS("GLUE(Nodejs)", true, "node", ".js"), |
| | | GLUE_POWERSHELL("GLUE(PowerShell)", true, "powershell", ".ps1"); |
| | | |
| | | private String desc; |
| | | private boolean isScript; |
| | | private String cmd; |
| | | private String suffix; |
| | | |
| | | private GlueTypeEnum(String desc, boolean isScript, String cmd, String suffix) { |
| | | this.desc = desc; |
| | | this.isScript = isScript; |
| | | this.cmd = cmd; |
| | | this.suffix = suffix; |
| | | } |
| | | |
| | | public String getDesc() { |
| | | return desc; |
| | | } |
| | | |
| | | public boolean isScript() { |
| | | return isScript; |
| | | } |
| | | |
| | | public String getCmd() { |
| | | return cmd; |
| | | } |
| | | |
| | | public String getSuffix() { |
| | | return suffix; |
| | | } |
| | | |
| | | public static GlueTypeEnum match(String name) { |
| | | for (GlueTypeEnum item : GlueTypeEnum.values()) { |
| | | if (item.name().equals(name)) { |
| | | return item; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.glue.impl; |
| | | |
| | | import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; |
| | | import com.xxl.job.core.glue.GlueFactory; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.core.annotation.AnnotationUtils; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.lang.reflect.Field; |
| | | import java.lang.reflect.Modifier; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class SpringGlueFactory extends GlueFactory { |
| | | private static Logger logger = LoggerFactory.getLogger(SpringGlueFactory.class); |
| | | |
| | | /** |
| | | * inject action of spring |
| | | * |
| | | * @param instance |
| | | */ |
| | | @Override |
| | | public void injectService(Object instance) { |
| | | if (instance == null) { |
| | | return; |
| | | } |
| | | |
| | | if (XxlJobSpringExecutor.getApplicationContext() == null) { |
| | | return; |
| | | } |
| | | |
| | | Field[] fields = instance.getClass().getDeclaredFields(); |
| | | for (Field field : fields) { |
| | | if (Modifier.isStatic(field.getModifiers())) { |
| | | continue; |
| | | } |
| | | |
| | | Object fieldBean = null; |
| | | // with bean-id, bean could be found by both @Resource and @Autowired, or bean could only be found by @Autowired |
| | | |
| | | if (AnnotationUtils.getAnnotation(field, Resource.class) != null) { |
| | | try { |
| | | Resource resource = AnnotationUtils.getAnnotation(field, Resource.class); |
| | | if (resource.name() != null && resource.name().length() > 0) { |
| | | fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(resource.name()); |
| | | } else { |
| | | fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getName()); |
| | | } |
| | | } catch (Exception e) { |
| | | } |
| | | if (fieldBean == null) { |
| | | fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getType()); |
| | | } |
| | | } else if (AnnotationUtils.getAnnotation(field, Autowired.class) != null) { |
| | | Qualifier qualifier = AnnotationUtils.getAnnotation(field, Qualifier.class); |
| | | if (qualifier != null && qualifier.value() != null && qualifier.value().length() > 0) { |
| | | fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(qualifier.value()); |
| | | } else { |
| | | fieldBean = XxlJobSpringExecutor.getApplicationContext().getBean(field.getType()); |
| | | } |
| | | } |
| | | |
| | | if (fieldBean != null) { |
| | | field.setAccessible(true); |
| | | try { |
| | | field.set(instance, fieldBean); |
| | | } catch (IllegalArgumentException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } catch (IllegalAccessException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.handler; |
| | | |
| | | /** |
| | | * job handler |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public abstract class IJobHandler { |
| | | |
| | | /** |
| | | * execute handler, invoked when executor receives a scheduling request |
| | | * |
| | | * @throws Exception |
| | | */ |
| | | public abstract void execute() throws Exception; |
| | | |
| | | /** |
| | | * init handler, invoked when JobThread init |
| | | */ |
| | | public void init() throws Exception { |
| | | |
| | | } |
| | | |
| | | /** |
| | | * destroy handler, invoked when JobThread destroy |
| | | */ |
| | | public void destroy() throws Exception { |
| | | |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.handler.annotation;//package com.xxl.job.core.handler.annotation;//package com.xxl.job.core.handler.annotation; |
| | | // |
| | | //import java.lang.annotation.ElementType; |
| | | //import java.lang.annotation.Inherited; |
| | | //import java.lang.annotation.Retention; |
| | | //import java.lang.annotation.RetentionPolicy; |
| | | //import java.lang.annotation.Target; |
| | | // |
| | | ///** |
| | | // * annotation for job handler |
| | | // * <p> |
| | | // * will be replaced by {@link com.xxl.job.core.handler.annotation.XxlJob} |
| | | // * |
| | | // * @author liyh |
| | | // */ |
| | | //@Target({ElementType.TYPE}) |
| | | //@Retention(RetentionPolicy.RUNTIME) |
| | | //@Inherited |
| | | //@Deprecated |
| | | //public @interface JobHandler { |
| | | // |
| | | // String value(); |
| | | // |
| | | //} |
| New file |
| | |
| | | package com.xxl.job.core.handler.annotation; |
| | | |
| | | import java.lang.annotation.*; |
| | | |
| | | /** |
| | | * annotation for method jobhandler |
| | | * |
| | | * @author liyh |
| | | */ |
| | | @Target({ElementType.METHOD}) |
| | | @Retention(RetentionPolicy.RUNTIME) |
| | | @Inherited |
| | | public @interface XxlJob { |
| | | |
| | | /** |
| | | * jobhandler name |
| | | */ |
| | | String value(); |
| | | |
| | | /** |
| | | * init handler, invoked when JobThread init |
| | | */ |
| | | String init() default ""; |
| | | |
| | | /** |
| | | * destroy handler, invoked when JobThread destroy |
| | | */ |
| | | String destroy() default ""; |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.handler.impl; |
| | | |
| | | import com.xxl.job.core.context.XxlJobHelper; |
| | | import com.xxl.job.core.handler.IJobHandler; |
| | | |
| | | /** |
| | | * glue job handler |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class GlueJobHandler extends IJobHandler { |
| | | |
| | | private long glueUpdatetime; |
| | | private IJobHandler jobHandler; |
| | | |
| | | public GlueJobHandler(IJobHandler jobHandler, long glueUpdatetime) { |
| | | this.jobHandler = jobHandler; |
| | | this.glueUpdatetime = glueUpdatetime; |
| | | } |
| | | |
| | | public long getGlueUpdatetime() { |
| | | return glueUpdatetime; |
| | | } |
| | | |
| | | @Override |
| | | public void execute() throws Exception { |
| | | XxlJobHelper.log("----------- glue.version:" + glueUpdatetime + " -----------"); |
| | | jobHandler.execute(); |
| | | } |
| | | |
| | | @Override |
| | | public void init() throws Exception { |
| | | this.jobHandler.init(); |
| | | } |
| | | |
| | | @Override |
| | | public void destroy() throws Exception { |
| | | this.jobHandler.destroy(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.handler.impl; |
| | | |
| | | import com.xxl.job.core.handler.IJobHandler; |
| | | |
| | | import java.lang.reflect.Method; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class MethodJobHandler extends IJobHandler { |
| | | |
| | | private final Object target; |
| | | private final Method method; |
| | | private Method initMethod; |
| | | private Method destroyMethod; |
| | | |
| | | public MethodJobHandler(Object target, Method method, Method initMethod, Method destroyMethod) { |
| | | this.target = target; |
| | | this.method = method; |
| | | |
| | | this.initMethod = initMethod; |
| | | this.destroyMethod = destroyMethod; |
| | | } |
| | | |
| | | @Override |
| | | public void execute() throws Exception { |
| | | Class<?>[] paramTypes = method.getParameterTypes(); |
| | | if (paramTypes.length > 0) { |
| | | method.invoke(target, new Object[paramTypes.length]); // method-param can not be primitive-types |
| | | } else { |
| | | method.invoke(target); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void init() throws Exception { |
| | | if (initMethod != null) { |
| | | initMethod.invoke(target); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void destroy() throws Exception { |
| | | if (destroyMethod != null) { |
| | | destroyMethod.invoke(target); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return super.toString() + "[" + target.getClass() + "#" + method.getName() + "]"; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.handler.impl; |
| | | |
| | | import com.xxl.job.core.context.XxlJobContext; |
| | | import com.xxl.job.core.context.XxlJobHelper; |
| | | import com.xxl.job.core.glue.GlueTypeEnum; |
| | | import com.xxl.job.core.handler.IJobHandler; |
| | | import com.xxl.job.core.log.XxlJobFileAppender; |
| | | import com.xxl.job.core.util.ScriptUtil; |
| | | |
| | | import java.io.File; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class ScriptJobHandler extends IJobHandler { |
| | | |
| | | private int jobId; |
| | | private long glueUpdatetime; |
| | | private String gluesource; |
| | | private GlueTypeEnum glueType; |
| | | |
| | | public ScriptJobHandler(int jobId, long glueUpdatetime, String gluesource, GlueTypeEnum glueType) { |
| | | this.jobId = jobId; |
| | | this.glueUpdatetime = glueUpdatetime; |
| | | this.gluesource = gluesource; |
| | | this.glueType = glueType; |
| | | |
| | | // clean old script file |
| | | File glueSrcPath = new File(XxlJobFileAppender.getGlueSrcPath()); |
| | | if (glueSrcPath.exists()) { |
| | | File[] glueSrcFileList = glueSrcPath.listFiles(); |
| | | if (glueSrcFileList != null && glueSrcFileList.length > 0) { |
| | | for (File glueSrcFileItem : glueSrcFileList) { |
| | | if (glueSrcFileItem.getName().startsWith(String.valueOf(jobId) + "_")) { |
| | | glueSrcFileItem.delete(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | public long getGlueUpdatetime() { |
| | | return glueUpdatetime; |
| | | } |
| | | |
| | | @Override |
| | | public void execute() throws Exception { |
| | | |
| | | if (!glueType.isScript()) { |
| | | XxlJobHelper.handleFail("glueType[" + glueType + "] invalid."); |
| | | return; |
| | | } |
| | | |
| | | // cmd |
| | | String cmd = glueType.getCmd(); |
| | | |
| | | // make script file |
| | | String scriptFileName = XxlJobFileAppender.getGlueSrcPath() |
| | | .concat(File.separator) |
| | | .concat(String.valueOf(jobId)) |
| | | .concat("_") |
| | | .concat(String.valueOf(glueUpdatetime)) |
| | | .concat(glueType.getSuffix()); |
| | | File scriptFile = new File(scriptFileName); |
| | | if (!scriptFile.exists()) { |
| | | ScriptUtil.markScriptFile(scriptFileName, gluesource); |
| | | } |
| | | |
| | | // log file |
| | | String logFileName = XxlJobContext.getXxlJobContext().getJobLogFileName(); |
| | | |
| | | // script params:0=param、1=分片序号、2=分片总数 |
| | | String[] scriptParams = new String[3]; |
| | | scriptParams[0] = XxlJobHelper.getJobParam(); |
| | | scriptParams[1] = String.valueOf(XxlJobContext.getXxlJobContext().getShardIndex()); |
| | | scriptParams[2] = String.valueOf(XxlJobContext.getXxlJobContext().getShardTotal()); |
| | | |
| | | // invoke |
| | | XxlJobHelper.log("----------- script file:" + scriptFileName + " -----------"); |
| | | int exitValue = ScriptUtil.execToFile(cmd, scriptFileName, logFileName, scriptParams); |
| | | |
| | | if (exitValue == 0) { |
| | | XxlJobHelper.handleSuccess(); |
| | | return; |
| | | } else { |
| | | XxlJobHelper.handleFail("script exit value(" + exitValue + ") is failed"); |
| | | return; |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.log; |
| | | |
| | | import com.xxl.job.core.biz.model.LogResult; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.io.*; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * store trigger log in each log-file |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class XxlJobFileAppender { |
| | | private static Logger logger = LoggerFactory.getLogger(XxlJobFileAppender.class); |
| | | |
| | | private static String logBasePath = "logs/xxl-job/jobhandler"; |
| | | private static String glueSrcPath = logBasePath.concat("/gluesource"); |
| | | |
| | | public static void initLogPath(String logPath) { |
| | | // init |
| | | if (logPath != null && logPath.trim().length() > 0) { |
| | | logBasePath = logPath; |
| | | } |
| | | // mk base dir |
| | | File logPathDir = new File(logBasePath); |
| | | if (!logPathDir.exists()) { |
| | | logPathDir.mkdirs(); |
| | | } |
| | | logBasePath = logPathDir.getPath(); |
| | | |
| | | // mk glue dir |
| | | File glueBaseDir = new File(logPathDir, "gluesource"); |
| | | if (!glueBaseDir.exists()) { |
| | | glueBaseDir.mkdirs(); |
| | | } |
| | | glueSrcPath = glueBaseDir.getPath(); |
| | | } |
| | | |
| | | public static String getLogPath() { |
| | | return logBasePath; |
| | | } |
| | | |
| | | public static String getGlueSrcPath() { |
| | | return glueSrcPath; |
| | | } |
| | | |
| | | /** |
| | | * log filename, like "logPath/yyyy-MM-dd/9999.log" |
| | | * |
| | | * @param triggerDate |
| | | * @param logId |
| | | * @return |
| | | */ |
| | | public static String makeLogFileName(Date triggerDate, long logId) { |
| | | |
| | | // filePath/yyyy-MM-dd |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // avoid concurrent problem, can not be static |
| | | File logFilePath = new File(getLogPath(), sdf.format(triggerDate)); |
| | | if (!logFilePath.exists()) { |
| | | logFilePath.mkdir(); |
| | | } |
| | | |
| | | // filePath/yyyy-MM-dd/9999.log |
| | | String logFileName = logFilePath.getPath() |
| | | .concat(File.separator) |
| | | .concat(String.valueOf(logId)) |
| | | .concat(".log"); |
| | | return logFileName; |
| | | } |
| | | |
| | | /** |
| | | * append log |
| | | * |
| | | * @param logFileName |
| | | * @param appendLog |
| | | */ |
| | | public static void appendLog(String logFileName, String appendLog) { |
| | | |
| | | // log file |
| | | if (logFileName == null || logFileName.trim().length() == 0) { |
| | | return; |
| | | } |
| | | File logFile = new File(logFileName); |
| | | |
| | | if (!logFile.exists()) { |
| | | try { |
| | | logFile.createNewFile(); |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // log |
| | | if (appendLog == null) { |
| | | appendLog = ""; |
| | | } |
| | | appendLog += "\r\n"; |
| | | |
| | | // append file content |
| | | FileOutputStream fos = null; |
| | | try { |
| | | fos = new FileOutputStream(logFile, true); |
| | | fos.write(appendLog.getBytes("utf-8")); |
| | | fos.flush(); |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | } finally { |
| | | if (fos != null) { |
| | | try { |
| | | fos.close(); |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * support read log-file |
| | | * |
| | | * @param logFileName |
| | | * @return log content |
| | | */ |
| | | public static LogResult readLog(String logFileName, int fromLineNum) { |
| | | |
| | | // valid log file |
| | | if (logFileName == null || logFileName.trim().length() == 0) { |
| | | return new LogResult(fromLineNum, 0, "readLog fail, logFile not found", true); |
| | | } |
| | | File logFile = new File(logFileName); |
| | | |
| | | if (!logFile.exists()) { |
| | | return new LogResult(fromLineNum, 0, "readLog fail, logFile not exists", true); |
| | | } |
| | | |
| | | // read file |
| | | StringBuffer logContentBuffer = new StringBuffer(); |
| | | int toLineNum = 0; |
| | | LineNumberReader reader = null; |
| | | try { |
| | | //reader = new LineNumberReader(new FileReader(logFile)); |
| | | reader = new LineNumberReader(new InputStreamReader(new FileInputStream(logFile), "utf-8")); |
| | | String line = null; |
| | | |
| | | while ((line = reader.readLine()) != null) { |
| | | toLineNum = reader.getLineNumber(); // [from, to], start as 1 |
| | | if (toLineNum >= fromLineNum) { |
| | | logContentBuffer.append(line).append("\n"); |
| | | } |
| | | } |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } finally { |
| | | if (reader != null) { |
| | | try { |
| | | reader.close(); |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // result |
| | | LogResult logResult = new LogResult(fromLineNum, toLineNum, logContentBuffer.toString(), false); |
| | | return logResult; |
| | | |
| | | /* |
| | | // it will return the number of characters actually skipped |
| | | reader.skip(Long.MAX_VALUE); |
| | | int maxLineNum = reader.getLineNumber(); |
| | | maxLineNum++; // 最大行号 |
| | | */ |
| | | } |
| | | |
| | | /** |
| | | * read log data |
| | | * |
| | | * @param logFile |
| | | * @return log line content |
| | | */ |
| | | public static String readLines(File logFile) { |
| | | BufferedReader reader = null; |
| | | try { |
| | | reader = new BufferedReader(new InputStreamReader(new FileInputStream(logFile), "utf-8")); |
| | | if (reader != null) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | String line = null; |
| | | while ((line = reader.readLine()) != null) { |
| | | sb.append(line).append("\n"); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } finally { |
| | | if (reader != null) { |
| | | try { |
| | | reader.close(); |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.server; |
| | | |
| | | import com.xxl.job.core.biz.ExecutorBiz; |
| | | import com.xxl.job.core.biz.impl.ExecutorBizImpl; |
| | | import com.xxl.job.core.biz.model.*; |
| | | import com.xxl.job.core.thread.ExecutorRegistryThread; |
| | | import com.xxl.job.core.util.GsonTool; |
| | | import com.xxl.job.core.util.ThrowableUtil; |
| | | import com.xxl.job.core.util.XxlJobRemotingUtil; |
| | | import io.netty.bootstrap.ServerBootstrap; |
| | | import io.netty.buffer.Unpooled; |
| | | import io.netty.channel.*; |
| | | import io.netty.channel.nio.NioEventLoopGroup; |
| | | import io.netty.channel.socket.SocketChannel; |
| | | import io.netty.channel.socket.nio.NioServerSocketChannel; |
| | | import io.netty.handler.codec.http.*; |
| | | import io.netty.handler.timeout.IdleStateEvent; |
| | | import io.netty.handler.timeout.IdleStateHandler; |
| | | import io.netty.util.CharsetUtil; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.util.concurrent.*; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class EmbedServer { |
| | | private static final Logger logger = LoggerFactory.getLogger(EmbedServer.class); |
| | | |
| | | private ExecutorBiz executorBiz; |
| | | private Thread thread; |
| | | |
| | | public void start(final String address, final int port, final String appname, final String accessToken) { |
| | | executorBiz = new ExecutorBizImpl(); |
| | | thread = new Thread(new Runnable() { |
| | | |
| | | @Override |
| | | public void run() { |
| | | |
| | | // param |
| | | EventLoopGroup bossGroup = new NioEventLoopGroup(); |
| | | EventLoopGroup workerGroup = new NioEventLoopGroup(); |
| | | ThreadPoolExecutor bizThreadPool = new ThreadPoolExecutor( |
| | | 0, |
| | | 200, |
| | | 60L, |
| | | TimeUnit.SECONDS, |
| | | new LinkedBlockingQueue<Runnable>(2000), |
| | | new ThreadFactory() { |
| | | @Override |
| | | public Thread newThread(Runnable r) { |
| | | return new Thread(r, "xxl-job, EmbedServer bizThreadPool-" + r.hashCode()); |
| | | } |
| | | }, |
| | | new RejectedExecutionHandler() { |
| | | @Override |
| | | public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { |
| | | throw new RuntimeException("xxl-job, EmbedServer bizThreadPool is EXHAUSTED!"); |
| | | } |
| | | }); |
| | | |
| | | |
| | | try { |
| | | // start server |
| | | ServerBootstrap bootstrap = new ServerBootstrap(); |
| | | bootstrap.group(bossGroup, workerGroup) |
| | | .channel(NioServerSocketChannel.class) |
| | | .childHandler(new ChannelInitializer<SocketChannel>() { |
| | | @Override |
| | | public void initChannel(SocketChannel channel) throws Exception { |
| | | channel.pipeline() |
| | | .addLast(new IdleStateHandler(0, 0, 30 * 3, TimeUnit.SECONDS)) // beat 3N, close if idle |
| | | .addLast(new HttpServerCodec()) |
| | | .addLast(new HttpObjectAggregator(5 * 1024 * 1024)) // merge request & reponse to FULL |
| | | .addLast(new EmbedHttpServerHandler(executorBiz, accessToken, bizThreadPool)); |
| | | } |
| | | }) |
| | | .childOption(ChannelOption.SO_KEEPALIVE, true); |
| | | |
| | | // bind |
| | | ChannelFuture future = bootstrap.bind(port).sync(); |
| | | |
| | | logger.info(">>>>>>>>>>> xxl-job remoting server start success, nettype = {}, port = {}", EmbedServer.class, port); |
| | | |
| | | // start registry |
| | | startRegistry(appname, address); |
| | | |
| | | // wait util stop |
| | | future.channel().closeFuture().sync(); |
| | | |
| | | } catch (InterruptedException e) { |
| | | if (e instanceof InterruptedException) { |
| | | logger.info(">>>>>>>>>>> xxl-job remoting server stop."); |
| | | } else { |
| | | logger.error(">>>>>>>>>>> xxl-job remoting server error.", e); |
| | | } |
| | | } finally { |
| | | // stop |
| | | try { |
| | | workerGroup.shutdownGracefully(); |
| | | bossGroup.shutdownGracefully(); |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | }); |
| | | thread.setDaemon(true); // daemon, service jvm, user thread leave >>> daemon leave >>> jvm leave |
| | | thread.start(); |
| | | } |
| | | |
| | | public void stop() throws Exception { |
| | | // destroy server thread |
| | | if (thread != null && thread.isAlive()) { |
| | | thread.interrupt(); |
| | | } |
| | | |
| | | // stop registry |
| | | stopRegistry(); |
| | | logger.info(">>>>>>>>>>> xxl-job remoting server destroy success."); |
| | | } |
| | | |
| | | |
| | | // ---------------------- registry ---------------------- |
| | | |
| | | /** |
| | | * netty_http |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public static class EmbedHttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| | | private static final Logger logger = LoggerFactory.getLogger(EmbedHttpServerHandler.class); |
| | | |
| | | private ExecutorBiz executorBiz; |
| | | private String accessToken; |
| | | private ThreadPoolExecutor bizThreadPool; |
| | | |
| | | public EmbedHttpServerHandler(ExecutorBiz executorBiz, String accessToken, ThreadPoolExecutor bizThreadPool) { |
| | | this.executorBiz = executorBiz; |
| | | this.accessToken = accessToken; |
| | | this.bizThreadPool = bizThreadPool; |
| | | } |
| | | |
| | | @Override |
| | | protected void channelRead0(final ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { |
| | | |
| | | // request parse |
| | | //final byte[] requestBytes = ByteBufUtil.getBytes(msg.content()); // byteBuf.toString(io.netty.util.CharsetUtil.UTF_8); |
| | | String requestData = msg.content().toString(CharsetUtil.UTF_8); |
| | | String uri = msg.uri(); |
| | | HttpMethod httpMethod = msg.method(); |
| | | boolean keepAlive = HttpUtil.isKeepAlive(msg); |
| | | String accessTokenReq = msg.headers().get(XxlJobRemotingUtil.XXL_JOB_ACCESS_TOKEN); |
| | | |
| | | // invoke |
| | | bizThreadPool.execute(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | // do invoke |
| | | Object responseObj = process(httpMethod, uri, requestData, accessTokenReq); |
| | | |
| | | // to json |
| | | String responseJson = GsonTool.toJson(responseObj); |
| | | |
| | | // write response |
| | | writeResponse(ctx, keepAlive, responseJson); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private Object process(HttpMethod httpMethod, String uri, String requestData, String accessTokenReq) { |
| | | |
| | | // valid |
| | | if (HttpMethod.POST != httpMethod) { |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "invalid request, HttpMethod not support."); |
| | | } |
| | | if (uri == null || uri.trim().length() == 0) { |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "invalid request, uri-mapping empty."); |
| | | } |
| | | if (accessToken != null |
| | | && accessToken.trim().length() > 0 |
| | | && !accessToken.equals(accessTokenReq)) { |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "The access token is wrong."); |
| | | } |
| | | |
| | | // services mapping |
| | | try { |
| | | if ("/beat".equals(uri)) { |
| | | return executorBiz.beat(); |
| | | } else if ("/idleBeat".equals(uri)) { |
| | | IdleBeatParam idleBeatParam = GsonTool.fromJson(requestData, IdleBeatParam.class); |
| | | return executorBiz.idleBeat(idleBeatParam); |
| | | } else if ("/run".equals(uri)) { |
| | | TriggerParam triggerParam = GsonTool.fromJson(requestData, TriggerParam.class); |
| | | return executorBiz.run(triggerParam); |
| | | } else if ("/kill".equals(uri)) { |
| | | KillParam killParam = GsonTool.fromJson(requestData, KillParam.class); |
| | | return executorBiz.kill(killParam); |
| | | } else if ("/log".equals(uri)) { |
| | | LogParam logParam = GsonTool.fromJson(requestData, LogParam.class); |
| | | return executorBiz.log(logParam); |
| | | } else { |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "invalid request, uri-mapping(" + uri + ") not found."); |
| | | } |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "request error:" + ThrowableUtil.toString(e)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * write response |
| | | */ |
| | | private void writeResponse(ChannelHandlerContext ctx, boolean keepAlive, String responseJson) { |
| | | // write response |
| | | FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(responseJson, CharsetUtil.UTF_8)); // Unpooled.wrappedBuffer(responseJson) |
| | | response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8"); // HttpHeaderValues.TEXT_PLAIN.toString() |
| | | response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); |
| | | if (keepAlive) { |
| | | response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); |
| | | } |
| | | ctx.writeAndFlush(response); |
| | | } |
| | | |
| | | @Override |
| | | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { |
| | | ctx.flush(); |
| | | } |
| | | |
| | | @Override |
| | | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { |
| | | logger.error(">>>>>>>>>>> xxl-job provider netty_http server caught exception", cause); |
| | | ctx.close(); |
| | | } |
| | | |
| | | @Override |
| | | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { |
| | | if (evt instanceof IdleStateEvent) { |
| | | ctx.channel().close(); // beat 3N, close if idle |
| | | logger.debug(">>>>>>>>>>> xxl-job provider netty_http server close an idle channel."); |
| | | } else { |
| | | super.userEventTriggered(ctx, evt); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // ---------------------- registry ---------------------- |
| | | |
| | | public void startRegistry(final String appname, final String address) { |
| | | // start registry |
| | | ExecutorRegistryThread.getInstance().start(appname, address); |
| | | } |
| | | |
| | | public void stopRegistry() { |
| | | // stop registry |
| | | ExecutorRegistryThread.getInstance().toStop(); |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.thread; |
| | | |
| | | import com.xxl.job.core.biz.AdminBiz; |
| | | import com.xxl.job.core.biz.model.RegistryParam; |
| | | import com.xxl.job.core.biz.model.ReturnT; |
| | | import com.xxl.job.core.enums.RegistryConfig; |
| | | import com.xxl.job.core.executor.XxlJobExecutor; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class ExecutorRegistryThread { |
| | | private static Logger logger = LoggerFactory.getLogger(ExecutorRegistryThread.class); |
| | | |
| | | private static ExecutorRegistryThread instance = new ExecutorRegistryThread(); |
| | | |
| | | public static ExecutorRegistryThread getInstance() { |
| | | return instance; |
| | | } |
| | | |
| | | private Thread registryThread; |
| | | private volatile boolean toStop = false; |
| | | |
| | | public void start(final String appname, final String address) { |
| | | |
| | | // valid |
| | | if (appname == null || appname.trim().length() == 0) { |
| | | logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, appname is null."); |
| | | return; |
| | | } |
| | | if (XxlJobExecutor.getAdminBizList() == null) { |
| | | logger.warn(">>>>>>>>>>> xxl-job, executor registry config fail, adminAddresses is null."); |
| | | return; |
| | | } |
| | | |
| | | registryThread = new Thread(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | |
| | | // registry |
| | | while (!toStop) { |
| | | try { |
| | | RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(), appname, address); |
| | | for (AdminBiz adminBiz : XxlJobExecutor.getAdminBizList()) { |
| | | try { |
| | | ReturnT<String> registryResult = adminBiz.registry(registryParam); |
| | | if (registryResult != null && ReturnT.SUCCESS_CODE == registryResult.getCode()) { |
| | | registryResult = ReturnT.SUCCESS; |
| | | logger.debug(">>>>>>>>>>> xxl-job registry success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult}); |
| | | break; |
| | | } else { |
| | | logger.info(">>>>>>>>>>> xxl-job registry fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult}); |
| | | } |
| | | } catch (Exception e) { |
| | | logger.info(">>>>>>>>>>> xxl-job registry error, registryParam:{}", registryParam, e); |
| | | } |
| | | |
| | | } |
| | | } catch (Exception e) { |
| | | if (!toStop) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | |
| | | } |
| | | |
| | | try { |
| | | if (!toStop) { |
| | | TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT); |
| | | } |
| | | } catch (InterruptedException e) { |
| | | if (!toStop) { |
| | | logger.warn(">>>>>>>>>>> xxl-job, executor registry thread interrupted, error msg:{}", e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // registry remove |
| | | try { |
| | | RegistryParam registryParam = new RegistryParam(RegistryConfig.RegistType.EXECUTOR.name(), appname, address); |
| | | for (AdminBiz adminBiz : XxlJobExecutor.getAdminBizList()) { |
| | | try { |
| | | ReturnT<String> registryResult = adminBiz.registryRemove(registryParam); |
| | | if (registryResult != null && ReturnT.SUCCESS_CODE == registryResult.getCode()) { |
| | | registryResult = ReturnT.SUCCESS; |
| | | logger.info(">>>>>>>>>>> xxl-job registry-remove success, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult}); |
| | | break; |
| | | } else { |
| | | logger.info(">>>>>>>>>>> xxl-job registry-remove fail, registryParam:{}, registryResult:{}", new Object[]{registryParam, registryResult}); |
| | | } |
| | | } catch (Exception e) { |
| | | if (!toStop) { |
| | | logger.info(">>>>>>>>>>> xxl-job registry-remove error, registryParam:{}", registryParam, e); |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | } catch (Exception e) { |
| | | if (!toStop) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | logger.info(">>>>>>>>>>> xxl-job, executor registry thread destroy."); |
| | | |
| | | } |
| | | }); |
| | | registryThread.setDaemon(true); |
| | | registryThread.setName("xxl-job, executor ExecutorRegistryThread"); |
| | | registryThread.start(); |
| | | } |
| | | |
| | | public void toStop() { |
| | | toStop = true; |
| | | |
| | | // interrupt and wait |
| | | if (registryThread != null) { |
| | | registryThread.interrupt(); |
| | | try { |
| | | registryThread.join(); |
| | | } catch (InterruptedException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.thread; |
| | | |
| | | import com.xxl.job.core.log.XxlJobFileAppender; |
| | | import com.xxl.job.core.util.FileUtil; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.io.File; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * job file clean thread |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class JobLogFileCleanThread { |
| | | private static Logger logger = LoggerFactory.getLogger(JobLogFileCleanThread.class); |
| | | |
| | | private static JobLogFileCleanThread instance = new JobLogFileCleanThread(); |
| | | |
| | | public static JobLogFileCleanThread getInstance() { |
| | | return instance; |
| | | } |
| | | |
| | | private Thread localThread; |
| | | private volatile boolean toStop = false; |
| | | |
| | | public void start(final long logRetentionDays) { |
| | | |
| | | // limit min value |
| | | if (logRetentionDays < 3) { |
| | | return; |
| | | } |
| | | |
| | | localThread = new Thread(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | while (!toStop) { |
| | | try { |
| | | // clean log dir, over logRetentionDays |
| | | File[] childDirs = new File(XxlJobFileAppender.getLogPath()).listFiles(); |
| | | if (childDirs != null && childDirs.length > 0) { |
| | | |
| | | // today |
| | | Calendar todayCal = Calendar.getInstance(); |
| | | todayCal.set(Calendar.HOUR_OF_DAY, 0); |
| | | todayCal.set(Calendar.MINUTE, 0); |
| | | todayCal.set(Calendar.SECOND, 0); |
| | | todayCal.set(Calendar.MILLISECOND, 0); |
| | | |
| | | Date todayDate = todayCal.getTime(); |
| | | |
| | | for (File childFile : childDirs) { |
| | | |
| | | // valid |
| | | if (!childFile.isDirectory()) { |
| | | continue; |
| | | } |
| | | if (childFile.getName().indexOf("-") == -1) { |
| | | continue; |
| | | } |
| | | |
| | | // file create date |
| | | Date logFileCreateDate = null; |
| | | try { |
| | | SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
| | | logFileCreateDate = simpleDateFormat.parse(childFile.getName()); |
| | | } catch (ParseException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | if (logFileCreateDate == null) { |
| | | continue; |
| | | } |
| | | |
| | | if ((todayDate.getTime() - logFileCreateDate.getTime()) >= logRetentionDays * (24 * 60 * 60 * 1000)) { |
| | | FileUtil.deleteRecursively(childFile); |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | if (!toStop) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | |
| | | } |
| | | |
| | | try { |
| | | TimeUnit.DAYS.sleep(1); |
| | | } catch (InterruptedException e) { |
| | | if (!toStop) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | logger.info(">>>>>>>>>>> xxl-job, executor JobLogFileCleanThread thread destroy."); |
| | | |
| | | } |
| | | }); |
| | | localThread.setDaemon(true); |
| | | localThread.setName("xxl-job, executor JobLogFileCleanThread"); |
| | | localThread.start(); |
| | | } |
| | | |
| | | public void toStop() { |
| | | toStop = true; |
| | | |
| | | if (localThread == null) { |
| | | return; |
| | | } |
| | | |
| | | // interrupt and wait |
| | | localThread.interrupt(); |
| | | try { |
| | | localThread.join(); |
| | | } catch (InterruptedException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.thread; |
| | | |
| | | import com.xxl.job.core.biz.model.HandleCallbackParam; |
| | | import com.xxl.job.core.biz.model.ReturnT; |
| | | import com.xxl.job.core.biz.model.TriggerParam; |
| | | import com.xxl.job.core.context.XxlJobContext; |
| | | import com.xxl.job.core.context.XxlJobHelper; |
| | | import com.xxl.job.core.executor.XxlJobExecutor; |
| | | import com.xxl.job.core.handler.IJobHandler; |
| | | import com.xxl.job.core.log.XxlJobFileAppender; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.io.PrintWriter; |
| | | import java.io.StringWriter; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.HashSet; |
| | | import java.util.Set; |
| | | import java.util.concurrent.*; |
| | | |
| | | |
| | | /** |
| | | * handler thread |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class JobThread extends Thread { |
| | | private static Logger logger = LoggerFactory.getLogger(JobThread.class); |
| | | |
| | | private int jobId; |
| | | private IJobHandler handler; |
| | | private LinkedBlockingQueue<TriggerParam> triggerQueue; |
| | | private Set<Long> triggerLogIdSet; // avoid repeat trigger for the same TRIGGER_LOG_ID |
| | | |
| | | private volatile boolean toStop = false; |
| | | private String stopReason; |
| | | |
| | | private boolean running = false; // if running job |
| | | private int idleTimes = 0; // idel times |
| | | |
| | | |
| | | public JobThread(int jobId, IJobHandler handler) { |
| | | this.jobId = jobId; |
| | | this.handler = handler; |
| | | this.triggerQueue = new LinkedBlockingQueue<TriggerParam>(); |
| | | this.triggerLogIdSet = Collections.synchronizedSet(new HashSet<Long>()); |
| | | |
| | | // assign job thread name |
| | | this.setName("xxl-job, JobThread-" + jobId + "-" + System.currentTimeMillis()); |
| | | } |
| | | |
| | | public IJobHandler getHandler() { |
| | | return handler; |
| | | } |
| | | |
| | | /** |
| | | * new trigger to queue |
| | | * |
| | | * @param triggerParam |
| | | * @return |
| | | */ |
| | | public ReturnT<String> pushTriggerQueue(TriggerParam triggerParam) { |
| | | // avoid repeat |
| | | if (triggerLogIdSet.contains(triggerParam.getLogId())) { |
| | | logger.info(">>>>>>>>>>> repeate trigger job, logId:{}", triggerParam.getLogId()); |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "repeate trigger job, logId:" + triggerParam.getLogId()); |
| | | } |
| | | |
| | | triggerLogIdSet.add(triggerParam.getLogId()); |
| | | triggerQueue.add(triggerParam); |
| | | return ReturnT.SUCCESS; |
| | | } |
| | | |
| | | /** |
| | | * kill job thread |
| | | * |
| | | * @param stopReason |
| | | */ |
| | | public void toStop(String stopReason) { |
| | | /** |
| | | * Thread.interrupt只支持终止线程的阻塞状态(wait、join、sleep), |
| | | * 在阻塞出抛出InterruptedException异常,但是并不会终止运行的线程本身; |
| | | * 所以需要注意,此处彻底销毁本线程,需要通过共享变量方式; |
| | | */ |
| | | this.toStop = true; |
| | | this.stopReason = stopReason; |
| | | } |
| | | |
| | | /** |
| | | * is running job |
| | | * |
| | | * @return |
| | | */ |
| | | public boolean isRunningOrHasQueue() { |
| | | return running || triggerQueue.size() > 0; |
| | | } |
| | | |
| | | @Override |
| | | public void run() { |
| | | |
| | | // init |
| | | try { |
| | | handler.init(); |
| | | } catch (Throwable e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | |
| | | // execute |
| | | while (!toStop) { |
| | | running = false; |
| | | idleTimes++; |
| | | |
| | | TriggerParam triggerParam = null; |
| | | try { |
| | | // to check toStop signal, we need cycle, so wo cannot use queue.take(), instand of poll(timeout) |
| | | triggerParam = triggerQueue.poll(3L, TimeUnit.SECONDS); |
| | | if (triggerParam != null) { |
| | | running = true; |
| | | idleTimes = 0; |
| | | triggerLogIdSet.remove(triggerParam.getLogId()); |
| | | |
| | | // log filename, like "logPath/yyyy-MM-dd/9999.log" |
| | | String logFileName = XxlJobFileAppender.makeLogFileName(new Date(triggerParam.getLogDateTime()), triggerParam.getLogId()); |
| | | XxlJobContext xxlJobContext = new XxlJobContext( |
| | | triggerParam.getJobId(), |
| | | triggerParam.getExecutorParams(), |
| | | logFileName, |
| | | triggerParam.getBroadcastIndex(), |
| | | triggerParam.getBroadcastTotal()); |
| | | |
| | | // init job context |
| | | XxlJobContext.setXxlJobContext(xxlJobContext); |
| | | |
| | | // execute |
| | | XxlJobHelper.log("<br>----------- xxl-job job execute start -----------<br>----------- Param:" + xxlJobContext.getJobParam()); |
| | | |
| | | if (triggerParam.getExecutorTimeout() > 0) { |
| | | // limit timeout |
| | | Thread futureThread = null; |
| | | try { |
| | | FutureTask<Boolean> futureTask = new FutureTask<Boolean>(new Callable<Boolean>() { |
| | | @Override |
| | | public Boolean call() throws Exception { |
| | | |
| | | // init job context |
| | | XxlJobContext.setXxlJobContext(xxlJobContext); |
| | | |
| | | handler.execute(); |
| | | return true; |
| | | } |
| | | }); |
| | | futureThread = new Thread(futureTask); |
| | | futureThread.start(); |
| | | |
| | | Boolean tempResult = futureTask.get(triggerParam.getExecutorTimeout(), TimeUnit.SECONDS); |
| | | } catch (TimeoutException e) { |
| | | |
| | | XxlJobHelper.log("<br>----------- xxl-job job execute timeout"); |
| | | XxlJobHelper.log(e); |
| | | |
| | | // handle result |
| | | XxlJobHelper.handleTimeout("job execute timeout "); |
| | | } finally { |
| | | futureThread.interrupt(); |
| | | } |
| | | } else { |
| | | // just execute |
| | | handler.execute(); |
| | | } |
| | | |
| | | // valid execute handle data |
| | | if (XxlJobContext.getXxlJobContext().getHandleCode() <= 0) { |
| | | XxlJobHelper.handleFail("job handle result lost."); |
| | | } else { |
| | | String tempHandleMsg = XxlJobContext.getXxlJobContext().getHandleMsg(); |
| | | tempHandleMsg = (tempHandleMsg != null && tempHandleMsg.length() > 50000) |
| | | ? tempHandleMsg.substring(0, 50000).concat("...") |
| | | : tempHandleMsg; |
| | | XxlJobContext.getXxlJobContext().setHandleMsg(tempHandleMsg); |
| | | } |
| | | XxlJobHelper.log("<br>----------- xxl-job job execute end(finish) -----------<br>----------- Result: handleCode=" |
| | | + XxlJobContext.getXxlJobContext().getHandleCode() |
| | | + ", handleMsg = " |
| | | + XxlJobContext.getXxlJobContext().getHandleMsg() |
| | | ); |
| | | |
| | | } else { |
| | | if (idleTimes > 30) { |
| | | if (triggerQueue.size() == 0) { // avoid concurrent trigger causes jobId-lost |
| | | XxlJobExecutor.removeJobThread(jobId, "excutor idel times over limit."); |
| | | } |
| | | } |
| | | } |
| | | } catch (Throwable e) { |
| | | if (toStop) { |
| | | XxlJobHelper.log("<br>----------- JobThread toStop, stopReason:" + stopReason); |
| | | } |
| | | |
| | | // handle result |
| | | StringWriter stringWriter = new StringWriter(); |
| | | e.printStackTrace(new PrintWriter(stringWriter)); |
| | | String errorMsg = stringWriter.toString(); |
| | | |
| | | XxlJobHelper.handleFail(errorMsg); |
| | | |
| | | XxlJobHelper.log("<br>----------- JobThread Exception:" + errorMsg + "<br>----------- xxl-job job execute end(error) -----------"); |
| | | } finally { |
| | | if (triggerParam != null) { |
| | | // callback handler info |
| | | if (!toStop) { |
| | | // commonm |
| | | TriggerCallbackThread.pushCallBack(new HandleCallbackParam( |
| | | triggerParam.getLogId(), |
| | | triggerParam.getLogDateTime(), |
| | | XxlJobContext.getXxlJobContext().getHandleCode(), |
| | | XxlJobContext.getXxlJobContext().getHandleMsg()) |
| | | ); |
| | | } else { |
| | | // is killed |
| | | TriggerCallbackThread.pushCallBack(new HandleCallbackParam( |
| | | triggerParam.getLogId(), |
| | | triggerParam.getLogDateTime(), |
| | | XxlJobContext.HANDLE_CODE_FAIL, |
| | | stopReason + " [job running, killed]") |
| | | ); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // callback trigger request in queue |
| | | while (triggerQueue != null && triggerQueue.size() > 0) { |
| | | TriggerParam triggerParam = triggerQueue.poll(); |
| | | if (triggerParam != null) { |
| | | // is killed |
| | | TriggerCallbackThread.pushCallBack(new HandleCallbackParam( |
| | | triggerParam.getLogId(), |
| | | triggerParam.getLogDateTime(), |
| | | XxlJobContext.HANDLE_CODE_FAIL, |
| | | stopReason + " [job not executed, in the job queue, killed.]") |
| | | ); |
| | | } |
| | | } |
| | | |
| | | // destroy |
| | | try { |
| | | handler.destroy(); |
| | | } catch (Throwable e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | |
| | | logger.info(">>>>>>>>>>> xxl-job JobThread stoped, hashCode:{}", Thread.currentThread()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.thread; |
| | | |
| | | import com.xxl.job.core.biz.AdminBiz; |
| | | import com.xxl.job.core.biz.model.HandleCallbackParam; |
| | | import com.xxl.job.core.biz.model.ReturnT; |
| | | import com.xxl.job.core.context.XxlJobContext; |
| | | import com.xxl.job.core.context.XxlJobHelper; |
| | | import com.xxl.job.core.enums.RegistryConfig; |
| | | import com.xxl.job.core.executor.XxlJobExecutor; |
| | | import com.xxl.job.core.log.XxlJobFileAppender; |
| | | import com.xxl.job.core.util.FileUtil; |
| | | import com.xxl.job.core.util.JdkSerializeTool; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.io.File; |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.concurrent.LinkedBlockingQueue; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class TriggerCallbackThread { |
| | | private static Logger logger = LoggerFactory.getLogger(TriggerCallbackThread.class); |
| | | |
| | | private static TriggerCallbackThread instance = new TriggerCallbackThread(); |
| | | |
| | | public static TriggerCallbackThread getInstance() { |
| | | return instance; |
| | | } |
| | | |
| | | /** |
| | | * job results callback queue |
| | | */ |
| | | private LinkedBlockingQueue<HandleCallbackParam> callBackQueue = new LinkedBlockingQueue<HandleCallbackParam>(); |
| | | |
| | | public static void pushCallBack(HandleCallbackParam callback) { |
| | | getInstance().callBackQueue.add(callback); |
| | | logger.debug(">>>>>>>>>>> xxl-job, push callback request, logId:{}", callback.getLogId()); |
| | | } |
| | | |
| | | /** |
| | | * callback thread |
| | | */ |
| | | private Thread triggerCallbackThread; |
| | | private Thread triggerRetryCallbackThread; |
| | | private volatile boolean toStop = false; |
| | | |
| | | public void start() { |
| | | |
| | | // valid |
| | | if (XxlJobExecutor.getAdminBizList() == null) { |
| | | logger.warn(">>>>>>>>>>> xxl-job, executor callback config fail, adminAddresses is null."); |
| | | return; |
| | | } |
| | | |
| | | // callback |
| | | triggerCallbackThread = new Thread(new Runnable() { |
| | | |
| | | @Override |
| | | public void run() { |
| | | |
| | | // normal callback |
| | | while (!toStop) { |
| | | try { |
| | | HandleCallbackParam callback = getInstance().callBackQueue.take(); |
| | | if (callback != null) { |
| | | |
| | | // callback list param |
| | | List<HandleCallbackParam> callbackParamList = new ArrayList<HandleCallbackParam>(); |
| | | int drainToNum = getInstance().callBackQueue.drainTo(callbackParamList); |
| | | callbackParamList.add(callback); |
| | | |
| | | // callback, will retry if error |
| | | if (callbackParamList != null && callbackParamList.size() > 0) { |
| | | doCallback(callbackParamList); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | if (!toStop) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // last callback |
| | | try { |
| | | List<HandleCallbackParam> callbackParamList = new ArrayList<HandleCallbackParam>(); |
| | | int drainToNum = getInstance().callBackQueue.drainTo(callbackParamList); |
| | | if (callbackParamList != null && callbackParamList.size() > 0) { |
| | | doCallback(callbackParamList); |
| | | } |
| | | } catch (Exception e) { |
| | | if (!toStop) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | logger.info(">>>>>>>>>>> xxl-job, executor callback thread destroy."); |
| | | |
| | | } |
| | | }); |
| | | triggerCallbackThread.setDaemon(true); |
| | | triggerCallbackThread.setName("xxl-job, executor TriggerCallbackThread"); |
| | | triggerCallbackThread.start(); |
| | | |
| | | |
| | | // retry |
| | | triggerRetryCallbackThread = new Thread(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | while (!toStop) { |
| | | try { |
| | | retryFailCallbackFile(); |
| | | } catch (Exception e) { |
| | | if (!toStop) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | |
| | | } |
| | | try { |
| | | TimeUnit.SECONDS.sleep(RegistryConfig.BEAT_TIMEOUT); |
| | | } catch (InterruptedException e) { |
| | | if (!toStop) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | logger.info(">>>>>>>>>>> xxl-job, executor retry callback thread destroy."); |
| | | } |
| | | }); |
| | | triggerRetryCallbackThread.setDaemon(true); |
| | | triggerRetryCallbackThread.start(); |
| | | |
| | | } |
| | | |
| | | public void toStop() { |
| | | toStop = true; |
| | | // stop callback, interrupt and wait |
| | | if (triggerCallbackThread != null) { // support empty admin address |
| | | triggerCallbackThread.interrupt(); |
| | | try { |
| | | triggerCallbackThread.join(); |
| | | } catch (InterruptedException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | // stop retry, interrupt and wait |
| | | if (triggerRetryCallbackThread != null) { |
| | | triggerRetryCallbackThread.interrupt(); |
| | | try { |
| | | triggerRetryCallbackThread.join(); |
| | | } catch (InterruptedException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * do callback, will retry if error |
| | | * |
| | | * @param callbackParamList |
| | | */ |
| | | private void doCallback(List<HandleCallbackParam> callbackParamList) { |
| | | boolean callbackRet = false; |
| | | // callback, will retry if error |
| | | for (AdminBiz adminBiz : XxlJobExecutor.getAdminBizList()) { |
| | | try { |
| | | ReturnT<String> callbackResult = adminBiz.callback(callbackParamList); |
| | | if (callbackResult != null && ReturnT.SUCCESS_CODE == callbackResult.getCode()) { |
| | | callbackLog(callbackParamList, "<br>----------- xxl-job job callback finish."); |
| | | callbackRet = true; |
| | | break; |
| | | } else { |
| | | callbackLog(callbackParamList, "<br>----------- xxl-job job callback fail, callbackResult:" + callbackResult); |
| | | } |
| | | } catch (Exception e) { |
| | | callbackLog(callbackParamList, "<br>----------- xxl-job job callback error, errorMsg:" + e.getMessage()); |
| | | } |
| | | } |
| | | if (!callbackRet) { |
| | | appendFailCallbackFile(callbackParamList); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * callback log |
| | | */ |
| | | private void callbackLog(List<HandleCallbackParam> callbackParamList, String logContent) { |
| | | for (HandleCallbackParam callbackParam : callbackParamList) { |
| | | String logFileName = XxlJobFileAppender.makeLogFileName(new Date(callbackParam.getLogDateTim()), callbackParam.getLogId()); |
| | | XxlJobContext.setXxlJobContext(new XxlJobContext( |
| | | -1, |
| | | null, |
| | | logFileName, |
| | | -1, |
| | | -1)); |
| | | XxlJobHelper.log(logContent); |
| | | } |
| | | } |
| | | |
| | | |
| | | // ---------------------- fail-callback file ---------------------- |
| | | |
| | | private static String failCallbackFilePath = XxlJobFileAppender.getLogPath().concat(File.separator).concat("callbacklog").concat(File.separator); |
| | | private static String failCallbackFileName = failCallbackFilePath.concat("xxl-job-callback-{x}").concat(".log"); |
| | | |
| | | private void appendFailCallbackFile(List<HandleCallbackParam> callbackParamList) { |
| | | // valid |
| | | if (callbackParamList == null || callbackParamList.size() == 0) { |
| | | return; |
| | | } |
| | | |
| | | // append file |
| | | byte[] callbackParamList_bytes = JdkSerializeTool.serialize(callbackParamList); |
| | | |
| | | File callbackLogFile = new File(failCallbackFileName.replace("{x}", String.valueOf(System.currentTimeMillis()))); |
| | | if (callbackLogFile.exists()) { |
| | | for (int i = 0; i < 100; i++) { |
| | | callbackLogFile = new File(failCallbackFileName.replace("{x}", String.valueOf(System.currentTimeMillis()).concat("-").concat(String.valueOf(i)))); |
| | | if (!callbackLogFile.exists()) { |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | FileUtil.writeFileContent(callbackLogFile, callbackParamList_bytes); |
| | | } |
| | | |
| | | private void retryFailCallbackFile() { |
| | | |
| | | // valid |
| | | File callbackLogPath = new File(failCallbackFilePath); |
| | | if (!callbackLogPath.exists()) { |
| | | return; |
| | | } |
| | | if (callbackLogPath.isFile()) { |
| | | callbackLogPath.delete(); |
| | | } |
| | | if (!(callbackLogPath.isDirectory() && callbackLogPath.list() != null && callbackLogPath.list().length > 0)) { |
| | | return; |
| | | } |
| | | |
| | | // load and clear file, retry |
| | | for (File callbaclLogFile : callbackLogPath.listFiles()) { |
| | | byte[] callbackParamList_bytes = FileUtil.readFileContent(callbaclLogFile); |
| | | |
| | | // avoid empty file |
| | | if (callbackParamList_bytes == null || callbackParamList_bytes.length < 1) { |
| | | callbaclLogFile.delete(); |
| | | continue; |
| | | } |
| | | |
| | | List<HandleCallbackParam> callbackParamList = (List<HandleCallbackParam>) JdkSerializeTool.deserialize(callbackParamList_bytes, List.class); |
| | | |
| | | callbaclLogFile.delete(); |
| | | doCallback(callbackParamList); |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.util; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.text.DateFormat; |
| | | import java.text.ParseException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * date util |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class DateUtil { |
| | | |
| | | // ---------------------- format parse ---------------------- |
| | | private static Logger logger = LoggerFactory.getLogger(DateUtil.class); |
| | | |
| | | private static final String DATE_FORMAT = "yyyy-MM-dd"; |
| | | private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; |
| | | |
| | | private static final ThreadLocal<Map<String, DateFormat>> dateFormatThreadLocal = new ThreadLocal<Map<String, DateFormat>>(); |
| | | |
| | | private static DateFormat getDateFormat(String pattern) { |
| | | if (pattern == null || pattern.trim().length() == 0) { |
| | | throw new IllegalArgumentException("pattern cannot be empty."); |
| | | } |
| | | |
| | | Map<String, DateFormat> dateFormatMap = dateFormatThreadLocal.get(); |
| | | if (dateFormatMap != null && dateFormatMap.containsKey(pattern)) { |
| | | return dateFormatMap.get(pattern); |
| | | } |
| | | |
| | | synchronized (dateFormatThreadLocal) { |
| | | if (dateFormatMap == null) { |
| | | dateFormatMap = new HashMap<String, DateFormat>(); |
| | | } |
| | | dateFormatMap.put(pattern, new SimpleDateFormat(pattern)); |
| | | dateFormatThreadLocal.set(dateFormatMap); |
| | | } |
| | | |
| | | return dateFormatMap.get(pattern); |
| | | } |
| | | |
| | | /** |
| | | * format datetime. like "yyyy-MM-dd" |
| | | * |
| | | * @param date |
| | | * @return |
| | | * @throws ParseException |
| | | */ |
| | | public static String formatDate(Date date) { |
| | | return format(date, DATE_FORMAT); |
| | | } |
| | | |
| | | /** |
| | | * format date. like "yyyy-MM-dd HH:mm:ss" |
| | | * |
| | | * @param date |
| | | * @return |
| | | * @throws ParseException |
| | | */ |
| | | public static String formatDateTime(Date date) { |
| | | return format(date, DATETIME_FORMAT); |
| | | } |
| | | |
| | | /** |
| | | * format date |
| | | * |
| | | * @param date |
| | | * @param patten |
| | | * @return |
| | | * @throws ParseException |
| | | */ |
| | | public static String format(Date date, String patten) { |
| | | return getDateFormat(patten).format(date); |
| | | } |
| | | |
| | | /** |
| | | * parse date string, like "yyyy-MM-dd HH:mm:s" |
| | | * |
| | | * @param dateString |
| | | * @return |
| | | * @throws ParseException |
| | | */ |
| | | public static Date parseDate(String dateString) { |
| | | return parse(dateString, DATE_FORMAT); |
| | | } |
| | | |
| | | /** |
| | | * parse datetime string, like "yyyy-MM-dd HH:mm:ss" |
| | | * |
| | | * @param dateString |
| | | * @return |
| | | * @throws ParseException |
| | | */ |
| | | public static Date parseDateTime(String dateString) { |
| | | return parse(dateString, DATETIME_FORMAT); |
| | | } |
| | | |
| | | /** |
| | | * parse date |
| | | * |
| | | * @param dateString |
| | | * @param pattern |
| | | * @return |
| | | * @throws ParseException |
| | | */ |
| | | public static Date parse(String dateString, String pattern) { |
| | | try { |
| | | Date date = getDateFormat(pattern).parse(dateString); |
| | | return date; |
| | | } catch (Exception e) { |
| | | logger.warn("parse date error, dateString = {}, pattern={}; errorMsg = {}", dateString, pattern, e.getMessage()); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | |
| | | // ---------------------- add date ---------------------- |
| | | |
| | | public static Date addYears(final Date date, final int amount) { |
| | | return add(date, Calendar.YEAR, amount); |
| | | } |
| | | |
| | | public static Date addMonths(final Date date, final int amount) { |
| | | return add(date, Calendar.MONTH, amount); |
| | | } |
| | | |
| | | public static Date addDays(final Date date, final int amount) { |
| | | return add(date, Calendar.DAY_OF_MONTH, amount); |
| | | } |
| | | |
| | | public static Date addHours(final Date date, final int amount) { |
| | | return add(date, Calendar.HOUR_OF_DAY, amount); |
| | | } |
| | | |
| | | public static Date addMinutes(final Date date, final int amount) { |
| | | return add(date, Calendar.MINUTE, amount); |
| | | } |
| | | |
| | | private static Date add(final Date date, final int calendarField, final int amount) { |
| | | if (date == null) { |
| | | return null; |
| | | } |
| | | final Calendar c = Calendar.getInstance(); |
| | | c.setTime(date); |
| | | c.add(calendarField, amount); |
| | | return c.getTime(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.util; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.FileOutputStream; |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * file tool |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class FileUtil { |
| | | private static Logger logger = LoggerFactory.getLogger(FileUtil.class); |
| | | |
| | | /** |
| | | * delete recursively |
| | | * |
| | | * @param root |
| | | * @return |
| | | */ |
| | | public static boolean deleteRecursively(File root) { |
| | | if (root != null && root.exists()) { |
| | | if (root.isDirectory()) { |
| | | File[] children = root.listFiles(); |
| | | if (children != null) { |
| | | for (File child : children) { |
| | | deleteRecursively(child); |
| | | } |
| | | } |
| | | } |
| | | return root.delete(); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | public static void deleteFile(String fileName) { |
| | | // file |
| | | File file = new File(fileName); |
| | | if (file.exists()) { |
| | | file.delete(); |
| | | } |
| | | } |
| | | |
| | | public static void writeFileContent(File file, byte[] data) { |
| | | |
| | | // file |
| | | if (!file.exists()) { |
| | | file.getParentFile().mkdirs(); |
| | | } |
| | | |
| | | // append file content |
| | | FileOutputStream fos = null; |
| | | try { |
| | | fos = new FileOutputStream(file); |
| | | fos.write(data); |
| | | fos.flush(); |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | } finally { |
| | | if (fos != null) { |
| | | try { |
| | | fos.close(); |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | public static byte[] readFileContent(File file) { |
| | | Long filelength = file.length(); |
| | | byte[] filecontent = new byte[filelength.intValue()]; |
| | | |
| | | FileInputStream in = null; |
| | | try { |
| | | in = new FileInputStream(file); |
| | | in.read(filecontent); |
| | | in.close(); |
| | | |
| | | return filecontent; |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | return null; |
| | | } finally { |
| | | if (in != null) { |
| | | try { |
| | | in.close(); |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.util; |
| | | |
| | | import com.google.gson.Gson; |
| | | import com.google.gson.GsonBuilder; |
| | | import com.google.gson.reflect.TypeToken; |
| | | |
| | | import java.lang.reflect.ParameterizedType; |
| | | import java.lang.reflect.Type; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class GsonTool { |
| | | |
| | | private static Gson gson = null; |
| | | |
| | | static { |
| | | gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); |
| | | } |
| | | |
| | | /** |
| | | * Object 转成 json |
| | | * |
| | | * @param src |
| | | * @return String |
| | | */ |
| | | public static String toJson(Object src) { |
| | | return gson.toJson(src); |
| | | } |
| | | |
| | | /** |
| | | * json 转成 特定的cls的Object |
| | | * |
| | | * @param json |
| | | * @param classOfT |
| | | * @return |
| | | */ |
| | | public static <T> T fromJson(String json, Class<T> classOfT) { |
| | | return gson.fromJson(json, classOfT); |
| | | } |
| | | |
| | | /** |
| | | * json 转成 特定的 rawClass<classOfT> 的Object |
| | | * |
| | | * @param json |
| | | * @param classOfT |
| | | * @param argClassOfT |
| | | * @return |
| | | */ |
| | | public static <T> T fromJson(String json, Class<T> classOfT, Class argClassOfT) { |
| | | Type type = new ParameterizedType4ReturnT(classOfT, new Class[]{argClassOfT}); |
| | | return gson.fromJson(json, type); |
| | | } |
| | | |
| | | public static class ParameterizedType4ReturnT implements ParameterizedType { |
| | | private final Class raw; |
| | | private final Type[] args; |
| | | |
| | | public ParameterizedType4ReturnT(Class raw, Type[] args) { |
| | | this.raw = raw; |
| | | this.args = args != null ? args : new Type[0]; |
| | | } |
| | | |
| | | @Override |
| | | public Type[] getActualTypeArguments() { |
| | | return args; |
| | | } |
| | | |
| | | @Override |
| | | public Type getRawType() { |
| | | return raw; |
| | | } |
| | | |
| | | @Override |
| | | public Type getOwnerType() { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * json 转成 特定的cls的list |
| | | * |
| | | * @param json |
| | | * @param classOfT |
| | | * @return |
| | | */ |
| | | public static <T> List<T> fromJsonList(String json, Class<T> classOfT) { |
| | | return gson.fromJson( |
| | | json, |
| | | new TypeToken<List<T>>() { |
| | | }.getType() |
| | | ); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.util; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.io.IOException; |
| | | import java.net.Inet6Address; |
| | | import java.net.InetAddress; |
| | | import java.net.NetworkInterface; |
| | | import java.net.UnknownHostException; |
| | | import java.util.Enumeration; |
| | | import java.util.regex.Pattern; |
| | | |
| | | /** |
| | | * ip tool |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class IpUtil { |
| | | private static final Logger logger = LoggerFactory.getLogger(IpUtil.class); |
| | | |
| | | private static final String ANYHOST_VALUE = "0.0.0.0"; |
| | | private static final String LOCALHOST_VALUE = "127.0.0.1"; |
| | | private static final Pattern IP_PATTERN = Pattern.compile("\\d{1,3}(\\.\\d{1,3}){3,5}$"); |
| | | |
| | | |
| | | private static volatile InetAddress LOCAL_ADDRESS = null; |
| | | |
| | | // ---------------------- valid ---------------------- |
| | | |
| | | private static InetAddress toValidAddress(InetAddress address) { |
| | | if (address instanceof Inet6Address) { |
| | | Inet6Address v6Address = (Inet6Address) address; |
| | | if (isPreferIPV6Address()) { |
| | | return normalizeV6Address(v6Address); |
| | | } |
| | | } |
| | | if (isValidV4Address(address)) { |
| | | return address; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private static boolean isPreferIPV6Address() { |
| | | return Boolean.getBoolean("java.net.preferIPv6Addresses"); |
| | | } |
| | | |
| | | /** |
| | | * valid Inet4Address |
| | | * |
| | | * @param address |
| | | * @return |
| | | */ |
| | | private static boolean isValidV4Address(InetAddress address) { |
| | | if (address == null || address.isLoopbackAddress()) { |
| | | return false; |
| | | } |
| | | String name = address.getHostAddress(); |
| | | boolean result = (name != null |
| | | && IP_PATTERN.matcher(name).matches() |
| | | && !ANYHOST_VALUE.equals(name) |
| | | && !LOCALHOST_VALUE.equals(name)); |
| | | return result; |
| | | } |
| | | |
| | | private static InetAddress normalizeV6Address(Inet6Address address) { |
| | | String addr = address.getHostAddress(); |
| | | int i = addr.lastIndexOf('%'); |
| | | if (i > 0) { |
| | | try { |
| | | return InetAddress.getByName(addr.substring(0, i) + '%' + address.getScopeId()); |
| | | } catch (UnknownHostException e) { |
| | | // ignore |
| | | logger.debug("Unknown IPV6 address: ", e); |
| | | } |
| | | } |
| | | return address; |
| | | } |
| | | |
| | | // ---------------------- find ip ---------------------- |
| | | |
| | | private static InetAddress getLocalAddress0() { |
| | | InetAddress localAddress = null; |
| | | try { |
| | | localAddress = InetAddress.getLocalHost(); |
| | | InetAddress addressItem = toValidAddress(localAddress); |
| | | if (addressItem != null) { |
| | | return addressItem; |
| | | } |
| | | } catch (Throwable e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | |
| | | try { |
| | | Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); |
| | | if (null == interfaces) { |
| | | return localAddress; |
| | | } |
| | | while (interfaces.hasMoreElements()) { |
| | | try { |
| | | NetworkInterface network = interfaces.nextElement(); |
| | | if (network.isLoopback() || network.isVirtual() || !network.isUp()) { |
| | | continue; |
| | | } |
| | | Enumeration<InetAddress> addresses = network.getInetAddresses(); |
| | | while (addresses.hasMoreElements()) { |
| | | try { |
| | | InetAddress addressItem = toValidAddress(addresses.nextElement()); |
| | | if (addressItem != null) { |
| | | try { |
| | | if (addressItem.isReachable(100)) { |
| | | return addressItem; |
| | | } |
| | | } catch (IOException e) { |
| | | // ignore |
| | | } |
| | | } |
| | | } catch (Throwable e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } catch (Throwable e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | } catch (Throwable e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | return localAddress; |
| | | } |
| | | |
| | | // ---------------------- tool ---------------------- |
| | | |
| | | /** |
| | | * Find first valid IP from local network card |
| | | * |
| | | * @return first valid local IP |
| | | */ |
| | | public static InetAddress getLocalAddress() { |
| | | if (LOCAL_ADDRESS != null) { |
| | | return LOCAL_ADDRESS; |
| | | } |
| | | InetAddress localAddress = getLocalAddress0(); |
| | | LOCAL_ADDRESS = localAddress; |
| | | return localAddress; |
| | | } |
| | | |
| | | /** |
| | | * get ip address |
| | | * |
| | | * @return String |
| | | */ |
| | | public static String getIp() { |
| | | return getLocalAddress().getHostAddress(); |
| | | } |
| | | |
| | | /** |
| | | * get ip:port |
| | | * |
| | | * @param port |
| | | * @return String |
| | | */ |
| | | public static String getIpPort(int port) { |
| | | String ip = getIp(); |
| | | return getIpPort(ip, port); |
| | | } |
| | | |
| | | public static String getIpPort(String ip, int port) { |
| | | if (ip == null) { |
| | | return null; |
| | | } |
| | | return ip.concat(":").concat(String.valueOf(port)); |
| | | } |
| | | |
| | | public static Object[] parseIpPort(String address) { |
| | | String[] array = address.split(":"); |
| | | |
| | | String host = array[0]; |
| | | int port = Integer.parseInt(array[1]); |
| | | |
| | | return new Object[]{host, port}; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.util; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.io.*; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class JdkSerializeTool { |
| | | private static Logger logger = LoggerFactory.getLogger(JdkSerializeTool.class); |
| | | |
| | | // ------------------------ serialize and unserialize ------------------------ |
| | | |
| | | /** |
| | | * 将对象-->byte[] (由于jedis中不支持直接存储object所以转换成byte[]存入) |
| | | * |
| | | * @param object |
| | | * @return |
| | | */ |
| | | public static byte[] serialize(Object object) { |
| | | ObjectOutputStream oos = null; |
| | | ByteArrayOutputStream baos = null; |
| | | try { |
| | | // 序列化 |
| | | baos = new ByteArrayOutputStream(); |
| | | oos = new ObjectOutputStream(baos); |
| | | oos.writeObject(object); |
| | | byte[] bytes = baos.toByteArray(); |
| | | return bytes; |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | } finally { |
| | | try { |
| | | oos.close(); |
| | | baos.close(); |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 将byte[] -->Object |
| | | * |
| | | * @param bytes |
| | | * @return |
| | | */ |
| | | public static <T> Object deserialize(byte[] bytes, Class<T> clazz) { |
| | | ByteArrayInputStream bais = null; |
| | | try { |
| | | // 反序列化 |
| | | bais = new ByteArrayInputStream(bytes); |
| | | ObjectInputStream ois = new ObjectInputStream(bais); |
| | | return ois.readObject(); |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | } finally { |
| | | try { |
| | | bais.close(); |
| | | } catch (IOException e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.util; |
| | | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import java.io.IOException; |
| | | import java.net.ServerSocket; |
| | | |
| | | /** |
| | | * net util |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class NetUtil { |
| | | private static Logger logger = LoggerFactory.getLogger(NetUtil.class); |
| | | |
| | | /** |
| | | * find avaliable port |
| | | * |
| | | * @param defaultPort |
| | | * @return |
| | | */ |
| | | public static int findAvailablePort(int defaultPort) { |
| | | int portTmp = defaultPort; |
| | | while (portTmp < 65535) { |
| | | if (!isPortUsed(portTmp)) { |
| | | return portTmp; |
| | | } else { |
| | | portTmp++; |
| | | } |
| | | } |
| | | portTmp = defaultPort--; |
| | | while (portTmp > 0) { |
| | | if (!isPortUsed(portTmp)) { |
| | | return portTmp; |
| | | } else { |
| | | portTmp--; |
| | | } |
| | | } |
| | | throw new RuntimeException("no available port."); |
| | | } |
| | | |
| | | /** |
| | | * check port used |
| | | * |
| | | * @param port |
| | | * @return |
| | | */ |
| | | public static boolean isPortUsed(int port) { |
| | | boolean used = false; |
| | | ServerSocket serverSocket = null; |
| | | try { |
| | | serverSocket = new ServerSocket(port); |
| | | used = false; |
| | | } catch (IOException e) { |
| | | logger.info(">>>>>>>>>>> xxl-job, port[{}] is in use.", port); |
| | | used = true; |
| | | } finally { |
| | | if (serverSocket != null) { |
| | | try { |
| | | serverSocket.close(); |
| | | } catch (IOException e) { |
| | | logger.info(""); |
| | | } |
| | | } |
| | | } |
| | | return used; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.util; |
| | | |
| | | import com.xxl.job.core.context.XxlJobHelper; |
| | | |
| | | import java.io.FileOutputStream; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.io.OutputStream; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 1、内嵌编译器如"PythonInterpreter"无法引用扩展包,因此推荐使用java调用控制台进程方式"Runtime.getRuntime().exec()"来运行脚本(shell或python); |
| | | * 2、因为通过java调用控制台进程方式实现,需要保证目标机器PATH路径正确配置对应编译器; |
| | | * 3、暂时脚本执行日志只能在脚本执行结束后一次性获取,无法保证实时性;因此为确保日志实时性,可改为将脚本打印的日志存储在指定的日志文件上; |
| | | * 4、python 异常输出优先级高于标准输出,体现在Log文件中,因此推荐通过logging方式打日志保持和异常信息一致;否则用prinf日志顺序会错乱 |
| | | * |
| | | * @author liyh |
| | | */ |
| | | public class ScriptUtil { |
| | | |
| | | /** |
| | | * make script file |
| | | * |
| | | * @param scriptFileName |
| | | * @param content |
| | | * @throws IOException |
| | | */ |
| | | public static void markScriptFile(String scriptFileName, String content) throws IOException { |
| | | // make file, filePath/gluesource/666-123456789.py |
| | | FileOutputStream fileOutputStream = null; |
| | | try { |
| | | fileOutputStream = new FileOutputStream(scriptFileName); |
| | | fileOutputStream.write(content.getBytes("UTF-8")); |
| | | fileOutputStream.close(); |
| | | } catch (Exception e) { |
| | | throw e; |
| | | } finally { |
| | | if (fileOutputStream != null) { |
| | | fileOutputStream.close(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 脚本执行,日志文件实时输出 |
| | | * |
| | | * @param command |
| | | * @param scriptFile |
| | | * @param logFile |
| | | * @param params |
| | | * @return |
| | | * @throws IOException |
| | | */ |
| | | public static int execToFile(String command, String scriptFile, String logFile, String... params) throws IOException { |
| | | |
| | | FileOutputStream fileOutputStream = null; |
| | | Thread inputThread = null; |
| | | Thread errThread = null; |
| | | try { |
| | | // file |
| | | fileOutputStream = new FileOutputStream(logFile, true); |
| | | |
| | | // command |
| | | List<String> cmdarray = new ArrayList<>(); |
| | | cmdarray.add(command); |
| | | cmdarray.add(scriptFile); |
| | | if (params != null && params.length > 0) { |
| | | for (String param : params) { |
| | | cmdarray.add(param); |
| | | } |
| | | } |
| | | String[] cmdarrayFinal = cmdarray.toArray(new String[cmdarray.size()]); |
| | | |
| | | // process-exec |
| | | final Process process = Runtime.getRuntime().exec(cmdarrayFinal); |
| | | |
| | | // log-thread |
| | | final FileOutputStream finalFileOutputStream = fileOutputStream; |
| | | inputThread = new Thread(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | try { |
| | | copy(process.getInputStream(), finalFileOutputStream, new byte[1024]); |
| | | } catch (IOException e) { |
| | | XxlJobHelper.log(e); |
| | | } |
| | | } |
| | | }); |
| | | errThread = new Thread(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | try { |
| | | copy(process.getErrorStream(), finalFileOutputStream, new byte[1024]); |
| | | } catch (IOException e) { |
| | | XxlJobHelper.log(e); |
| | | } |
| | | } |
| | | }); |
| | | inputThread.start(); |
| | | errThread.start(); |
| | | |
| | | // process-wait |
| | | int exitValue = process.waitFor(); // exit code: 0=success, 1=error |
| | | |
| | | // log-thread join |
| | | inputThread.join(); |
| | | errThread.join(); |
| | | |
| | | return exitValue; |
| | | } catch (Exception e) { |
| | | XxlJobHelper.log(e); |
| | | return -1; |
| | | } finally { |
| | | if (fileOutputStream != null) { |
| | | try { |
| | | fileOutputStream.close(); |
| | | } catch (IOException e) { |
| | | XxlJobHelper.log(e); |
| | | } |
| | | |
| | | } |
| | | if (inputThread != null && inputThread.isAlive()) { |
| | | inputThread.interrupt(); |
| | | } |
| | | if (errThread != null && errThread.isAlive()) { |
| | | errThread.interrupt(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 数据流Copy(Input自动关闭,Output不处理) |
| | | * |
| | | * @param inputStream |
| | | * @param outputStream |
| | | * @param buffer |
| | | * @return |
| | | * @throws IOException |
| | | */ |
| | | private static long copy(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws IOException { |
| | | try { |
| | | long total = 0; |
| | | for (; ; ) { |
| | | int res = inputStream.read(buffer); |
| | | if (res == -1) { |
| | | break; |
| | | } |
| | | if (res > 0) { |
| | | total += res; |
| | | if (outputStream != null) { |
| | | outputStream.write(buffer, 0, res); |
| | | } |
| | | } |
| | | } |
| | | outputStream.flush(); |
| | | //out = null; |
| | | inputStream.close(); |
| | | inputStream = null; |
| | | return total; |
| | | } finally { |
| | | if (inputStream != null) { |
| | | inputStream.close(); |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.util; |
| | | |
| | | import java.io.PrintWriter; |
| | | import java.io.StringWriter; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class ThrowableUtil { |
| | | |
| | | /** |
| | | * parse error to string |
| | | * |
| | | * @param e |
| | | * @return |
| | | */ |
| | | public static String toString(Throwable e) { |
| | | StringWriter stringWriter = new StringWriter(); |
| | | e.printStackTrace(new PrintWriter(stringWriter)); |
| | | String errorMsg = stringWriter.toString(); |
| | | return errorMsg; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.xxl.job.core.util; |
| | | |
| | | import com.xxl.job.core.biz.model.ReturnT; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | import javax.net.ssl.*; |
| | | import java.io.BufferedReader; |
| | | import java.io.DataOutputStream; |
| | | import java.io.InputStreamReader; |
| | | import java.net.HttpURLConnection; |
| | | import java.net.URL; |
| | | import java.security.cert.CertificateException; |
| | | import java.security.cert.X509Certificate; |
| | | |
| | | /** |
| | | * @author liyh |
| | | */ |
| | | public class XxlJobRemotingUtil { |
| | | private static Logger logger = LoggerFactory.getLogger(XxlJobRemotingUtil.class); |
| | | public static final String XXL_JOB_ACCESS_TOKEN = "XXL-JOB-ACCESS-TOKEN"; |
| | | |
| | | // trust-https start |
| | | private static void trustAllHosts(HttpsURLConnection connection) { |
| | | try { |
| | | SSLContext sc = SSLContext.getInstance("TLS"); |
| | | sc.init(null, trustAllCerts, new java.security.SecureRandom()); |
| | | SSLSocketFactory newFactory = sc.getSocketFactory(); |
| | | |
| | | connection.setSSLSocketFactory(newFactory); |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | } |
| | | connection.setHostnameVerifier(new HostnameVerifier() { |
| | | @Override |
| | | public boolean verify(String hostname, SSLSession session) { |
| | | return true; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private static final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { |
| | | @Override |
| | | public X509Certificate[] getAcceptedIssuers() { |
| | | return new X509Certificate[]{}; |
| | | } |
| | | |
| | | @Override |
| | | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
| | | } |
| | | |
| | | @Override |
| | | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
| | | } |
| | | }}; |
| | | |
| | | /** |
| | | * post |
| | | * |
| | | * @param url |
| | | * @param accessToken |
| | | * @param timeout |
| | | * @param requestObj |
| | | * @param returnTargClassOfT |
| | | * @return |
| | | */ |
| | | public static ReturnT postBody(String url, String accessToken, int timeout, Object requestObj, Class returnTargClassOfT) { |
| | | HttpURLConnection connection = null; |
| | | BufferedReader bufferedReader = null; |
| | | try { |
| | | // connection |
| | | URL realUrl = new URL(url); |
| | | connection = (HttpURLConnection) realUrl.openConnection(); |
| | | |
| | | // trust-https |
| | | boolean useHttps = url.startsWith("https"); |
| | | if (useHttps) { |
| | | HttpsURLConnection https = (HttpsURLConnection) connection; |
| | | trustAllHosts(https); |
| | | } |
| | | |
| | | // connection setting |
| | | connection.setRequestMethod("POST"); |
| | | connection.setDoOutput(true); |
| | | connection.setDoInput(true); |
| | | connection.setUseCaches(false); |
| | | connection.setReadTimeout(timeout * 1000); |
| | | connection.setConnectTimeout(3 * 1000); |
| | | connection.setRequestProperty("connection", "Keep-Alive"); |
| | | connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); |
| | | connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8"); |
| | | |
| | | if (accessToken != null && accessToken.trim().length() > 0) { |
| | | connection.setRequestProperty(XXL_JOB_ACCESS_TOKEN, accessToken); |
| | | } |
| | | |
| | | // do connection |
| | | connection.connect(); |
| | | |
| | | // write requestBody |
| | | if (requestObj != null) { |
| | | String requestBody = GsonTool.toJson(requestObj); |
| | | |
| | | DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); |
| | | dataOutputStream.write(requestBody.getBytes("UTF-8")); |
| | | dataOutputStream.flush(); |
| | | dataOutputStream.close(); |
| | | } |
| | | |
| | | /*byte[] requestBodyBytes = requestBody.getBytes("UTF-8"); |
| | | connection.setRequestProperty("Content-Length", String.valueOf(requestBodyBytes.length)); |
| | | OutputStream outwritestream = connection.getOutputStream(); |
| | | outwritestream.write(requestBodyBytes); |
| | | outwritestream.flush(); |
| | | outwritestream.close();*/ |
| | | |
| | | // valid StatusCode |
| | | int statusCode = connection.getResponseCode(); |
| | | if (statusCode != 200) { |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "xxl-job remoting fail, StatusCode(" + statusCode + ") invalid. for url : " + url); |
| | | } |
| | | |
| | | // result |
| | | bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); |
| | | StringBuilder result = new StringBuilder(); |
| | | String line; |
| | | while ((line = bufferedReader.readLine()) != null) { |
| | | result.append(line); |
| | | } |
| | | String resultJson = result.toString(); |
| | | |
| | | // parse returnT |
| | | try { |
| | | ReturnT returnT = GsonTool.fromJson(resultJson, ReturnT.class, returnTargClassOfT); |
| | | return returnT; |
| | | } catch (Exception e) { |
| | | logger.error("xxl-job remoting (url=" + url + ") response content invalid(" + resultJson + ").", e); |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "xxl-job remoting (url=" + url + ") response content invalid(" + resultJson + ")."); |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | logger.error(e.getMessage(), e); |
| | | return new ReturnT<String>(ReturnT.FAIL_CODE, "xxl-job remoting error(" + e.getMessage() + "), for url : " + url); |
| | | } finally { |
| | | try { |
| | | if (bufferedReader != null) { |
| | | bufferedReader.close(); |
| | | } |
| | | if (connection != null) { |
| | | connection.disconnect(); |
| | | } |
| | | } catch (Exception e2) { |
| | | logger.error(e2.getMessage(), e2); |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package org.springblade.xxljob.config; |
| | | |
| | | import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | /** |
| | | * xxl-job config |
| | | * |
| | | * @author liyh |
| | | */ |
| | | @Configuration |
| | | public class XxlJobConfig { |
| | | private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); |
| | | |
| | | @Value("${xxl.job.admin.addresses}") |
| | | private String adminAddresses; |
| | | |
| | | @Value("${xxl.job.accessToken}") |
| | | private String accessToken; |
| | | |
| | | @Value("${xxl.job.executor.appname}") |
| | | private String appname; |
| | | |
| | | @Value("${xxl.job.executor.address}") |
| | | private String address; |
| | | |
| | | @Value("${xxl.job.executor.ip}") |
| | | private String ip; |
| | | |
| | | @Value("${xxl.job.executor.port}") |
| | | private int port; |
| | | |
| | | @Value("${xxl.job.executor.logpath}") |
| | | private String logPath; |
| | | |
| | | @Value("${xxl.job.executor.logretentiondays}") |
| | | private int logRetentionDays; |
| | | |
| | | @Bean |
| | | public XxlJobSpringExecutor xxlJobExecutor() { |
| | | logger.info(">>>>>>>>>>> xxl-job config init."); |
| | | XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); |
| | | xxlJobSpringExecutor.setAdminAddresses(adminAddresses); |
| | | xxlJobSpringExecutor.setAppname(appname); |
| | | xxlJobSpringExecutor.setAddress(address); |
| | | xxlJobSpringExecutor.setIp(ip); |
| | | xxlJobSpringExecutor.setPort(port); |
| | | xxlJobSpringExecutor.setAccessToken(accessToken); |
| | | xxlJobSpringExecutor.setLogPath(logPath); |
| | | xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); |
| | | |
| | | return xxlJobSpringExecutor; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package org.springblade.xxljob.jobhandler; |
| | | |
| | | import com.xxl.job.core.context.XxlJobHelper; |
| | | import com.xxl.job.core.handler.annotation.XxlJob; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.io.BufferedInputStream; |
| | | import java.io.BufferedReader; |
| | | import java.io.DataOutputStream; |
| | | import java.io.InputStreamReader; |
| | | import java.net.HttpURLConnection; |
| | | import java.net.URL; |
| | | import java.util.Arrays; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * XxlJob开发示例(Bean模式) |
| | | * <p> |
| | | * 开发步骤: |
| | | * 1、任务开发:在Spring Bean实例中,开发Job方法; |
| | | * 2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。 |
| | | * 3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志; |
| | | * 4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果; |
| | | * |
| | | * @author liyh |
| | | */ |
| | | @Component |
| | | public class SampleXxlJob { |
| | | private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class); |
| | | |
| | | /** |
| | | * 1、简单任务示例(Bean模式) |
| | | */ |
| | | @XxlJob("demoJobHandler") |
| | | public void demoJobHandler() throws Exception { |
| | | XxlJobHelper.log("XXL-JOB, Hello World."); |
| | | |
| | | for (int i = 0; i < 5; i++) { |
| | | XxlJobHelper.log("beat at:" + i); |
| | | System.out.println("hello" + i); |
| | | TimeUnit.SECONDS.sleep(2); |
| | | } |
| | | System.out.println("完成了"); |
| | | } |
| | | |
| | | @XxlJob("testJobHandler") |
| | | public void testJobHandler() throws Exception { |
| | | String jobParam = XxlJobHelper.getJobParam(); |
| | | System.out.println("执行成功,参数:" + jobParam); |
| | | } |
| | | |
| | | /** |
| | | * 2、分片广播任务 |
| | | */ |
| | | @XxlJob("shardingJobHandler") |
| | | public void shardingJobHandler() throws Exception { |
| | | |
| | | // 分片参数 |
| | | int shardIndex = XxlJobHelper.getShardIndex(); |
| | | int shardTotal = XxlJobHelper.getShardTotal(); |
| | | |
| | | XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); |
| | | |
| | | // 业务逻辑 |
| | | for (int i = 0; i < shardTotal; i++) { |
| | | if (i == shardIndex) { |
| | | XxlJobHelper.log("第 {} 片, 命中分片开始处理", i); |
| | | } else { |
| | | XxlJobHelper.log("第 {} 片, 忽略", i); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 3、命令行任务 |
| | | */ |
| | | @XxlJob("commandJobHandler") |
| | | public void commandJobHandler() throws Exception { |
| | | String command = XxlJobHelper.getJobParam(); |
| | | int exitValue = -1; |
| | | |
| | | BufferedReader bufferedReader = null; |
| | | try { |
| | | // command process |
| | | ProcessBuilder processBuilder = new ProcessBuilder(); |
| | | processBuilder.command(command); |
| | | processBuilder.redirectErrorStream(true); |
| | | |
| | | Process process = processBuilder.start(); |
| | | //Process process = Runtime.getRuntime().exec(command); |
| | | |
| | | BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); |
| | | bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); |
| | | |
| | | // command log |
| | | String line; |
| | | while ((line = bufferedReader.readLine()) != null) { |
| | | XxlJobHelper.log(line); |
| | | } |
| | | |
| | | // command exit |
| | | process.waitFor(); |
| | | exitValue = process.exitValue(); |
| | | } catch (Exception e) { |
| | | XxlJobHelper.log(e); |
| | | } finally { |
| | | if (bufferedReader != null) { |
| | | bufferedReader.close(); |
| | | } |
| | | } |
| | | |
| | | if (exitValue == 0) { |
| | | // default success |
| | | } else { |
| | | XxlJobHelper.handleFail("command exit value(" + exitValue + ") is failed"); |
| | | } |
| | | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 4、跨平台Http任务 |
| | | * 参数示例: |
| | | * "url: http://www.baidu.com\n" + |
| | | * "method: get\n" + |
| | | * "data: content\n"; |
| | | */ |
| | | @XxlJob("httpJobHandler") |
| | | public void httpJobHandler() throws Exception { |
| | | |
| | | // param parse |
| | | String param = XxlJobHelper.getJobParam(); |
| | | if (param == null || param.trim().length() == 0) { |
| | | XxlJobHelper.log("param[" + param + "] invalid."); |
| | | |
| | | XxlJobHelper.handleFail(); |
| | | return; |
| | | } |
| | | |
| | | String[] httpParams = param.split("\n"); |
| | | String url = null; |
| | | String method = null; |
| | | String data = null; |
| | | for (String httpParam : httpParams) { |
| | | if (httpParam.startsWith("url:")) { |
| | | url = httpParam.substring(httpParam.indexOf("url:") + 4).trim(); |
| | | } |
| | | if (httpParam.startsWith("method:")) { |
| | | method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase(); |
| | | } |
| | | if (httpParam.startsWith("data:")) { |
| | | data = httpParam.substring(httpParam.indexOf("data:") + 5).trim(); |
| | | } |
| | | } |
| | | |
| | | // param valid |
| | | if (url == null || url.trim().length() == 0) { |
| | | XxlJobHelper.log("url[" + url + "] invalid."); |
| | | |
| | | XxlJobHelper.handleFail(); |
| | | return; |
| | | } |
| | | if (method == null || !Arrays.asList("GET", "POST").contains(method)) { |
| | | XxlJobHelper.log("method[" + method + "] invalid."); |
| | | |
| | | XxlJobHelper.handleFail(); |
| | | return; |
| | | } |
| | | boolean isPostMethod = method.equals("POST"); |
| | | |
| | | // request |
| | | HttpURLConnection connection = null; |
| | | BufferedReader bufferedReader = null; |
| | | try { |
| | | // connection |
| | | URL realUrl = new URL(url); |
| | | connection = (HttpURLConnection) realUrl.openConnection(); |
| | | |
| | | // connection setting |
| | | connection.setRequestMethod(method); |
| | | connection.setDoOutput(isPostMethod); |
| | | connection.setDoInput(true); |
| | | connection.setUseCaches(false); |
| | | connection.setReadTimeout(5 * 1000); |
| | | connection.setConnectTimeout(3 * 1000); |
| | | connection.setRequestProperty("connection", "Keep-Alive"); |
| | | connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); |
| | | connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8"); |
| | | |
| | | // do connection |
| | | connection.connect(); |
| | | |
| | | // data |
| | | if (isPostMethod && data != null && data.trim().length() > 0) { |
| | | DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); |
| | | dataOutputStream.write(data.getBytes("UTF-8")); |
| | | dataOutputStream.flush(); |
| | | dataOutputStream.close(); |
| | | } |
| | | |
| | | // valid StatusCode |
| | | int statusCode = connection.getResponseCode(); |
| | | if (statusCode != 200) { |
| | | throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid."); |
| | | } |
| | | |
| | | // result |
| | | bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); |
| | | StringBuilder result = new StringBuilder(); |
| | | String line; |
| | | while ((line = bufferedReader.readLine()) != null) { |
| | | result.append(line); |
| | | } |
| | | String responseMsg = result.toString(); |
| | | |
| | | XxlJobHelper.log(responseMsg); |
| | | |
| | | return; |
| | | } catch (Exception e) { |
| | | XxlJobHelper.log(e); |
| | | |
| | | XxlJobHelper.handleFail(); |
| | | return; |
| | | } finally { |
| | | try { |
| | | if (bufferedReader != null) { |
| | | bufferedReader.close(); |
| | | } |
| | | if (connection != null) { |
| | | connection.disconnect(); |
| | | } |
| | | } catch (Exception e2) { |
| | | XxlJobHelper.log(e2); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑; |
| | | */ |
| | | @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy") |
| | | public void demoJobHandler2() throws Exception { |
| | | XxlJobHelper.log("XXL-JOB, Hello World."); |
| | | } |
| | | |
| | | public void init() { |
| | | logger.info("init"); |
| | | } |
| | | |
| | | public void destroy() { |
| | | logger.info("destroy"); |
| | | } |
| | | |
| | | } |
| | |
| | | upload-domain: http://localhost:8999 |
| | | remote-path: /usr/share/nginx/html |
| | | |
| | | # xxl-job |
| | | xxl: |
| | | job: |
| | | accessToken: '' |
| | | admin: |
| | | addresses: http://192.168.1.50:7009/xxl-job-admin |
| | | executor: |
| | | appname: blade-xxljob |
| | | ip: 127.0.0.1 |
| | | logpath: ../data/applogs/xxl-job/jobhandler |
| | | logretentiondays: -1 |
| | | port: 7018 |
| | | address: |
| | |
| | | remote-mode: true |
| | | upload-domain: http://localhost:8999 |
| | | remote-path: /usr/share/nginx/html |
| | | |
| | | # xxl-job |
| | | xxl: |
| | | job: |
| | | accessToken: '' |
| | | admin: |
| | | addresses: http://127.0.0.1:7009/xxl-job-admin |
| | | executor: |
| | | appname: blade-xxljob |
| | | ip: 127.0.0.1 |
| | | logpath: /app/server/xxl-job/jobhandler |
| | | logretentiondays: -1 |
| | | port: 7018 |
| | | address: |
| | |
| | | remote-mode: true |
| | | upload-domain: http://localhost:8999 |
| | | remote-path: /usr/share/nginx/html |
| | | |
| | | # xxl-job |
| | | xxl: |
| | | job: |
| | | accessToken: '' |
| | | admin: |
| | | addresses: http://192.168.1.50:7009/xxl-job-admin |
| | | executor: |
| | | appname: blade-xxljob |
| | | ip: 127.0.0.1 |
| | | logpath: ../data/applogs/xxl-job/jobhandler |
| | | logretentiondays: -1 |
| | | port: 7018 |
| | | address: |